Czy ktoś ma skrypt powłoki szablonów do robienia czegoś z ls
listą nazw katalogów i do przechodzenia między nimi i robienia czegoś?
Planuję zrobić, ls -1d */
aby uzyskać listę nazw katalogów.
Czy ktoś ma skrypt powłoki szablonów do robienia czegoś z ls
listą nazw katalogów i do przechodzenia między nimi i robienia czegoś?
Planuję zrobić, ls -1d */
aby uzyskać listę nazw katalogów.
Odpowiedzi:
Edytowano, aby nie używać ls tam, gdzie zrobiłby glob, jak sugerowali @ shawn-j-goff i inni.
Wystarczy użyć for..do..done
pętli:
for f in *; do
echo "File -> $f"
done
Można wymienić *
z *.txt
lub dowolny inny glob, która zwraca listę (plików, katalogów lub cokolwiek na ten temat), polecenie, które generuje listę, na przykład $(cat filelist.txt)
, czy rzeczywiście należy wymienić go na liście.
W do
pętli po prostu odwołujesz się do zmiennej pętli z prefiksem znaku dolara (tak $f
w powyższym przykładzie). Możesz echo
to zrobić lub zrobić cokolwiek innego, co chcesz.
Na przykład, aby zmienić nazwę wszystkich .xml
plików w bieżącym katalogu na .txt
:
for x in *.xml; do
t=$(echo $x | sed 's/\.xml$/.txt/');
mv $x $t && echo "moved $x -> $t"
done
Lub jeszcze lepiej, jeśli używasz Bash, możesz użyć rozszerzeń parametrów Bash zamiast odradzania podpowłoki:
for x in *.xml; do
t=${x%.xml}.txt
mv $x $t && echo "moved $x -> $t"
done
for
pętli i typową pułapkę podczas próby przetworzenia wyniku ls
. @ DanielA.White, to może rozważyć unaccepting tę odpowiedź, jeśli nie był pomocny (lub potencjalnie mylące), ponieważ jak powiedziałeś, jesteś działając w katalogach. Odpowiedź Shawna J. Goffa powinna zapewnić bardziej niezawodne i działające rozwiązanie twojego problemu.
ls
danych wyjściowych , nie powinieneś czytać danych wyjściowych w for
pętli , powinieneś użyć $()
zamiast `` i Użyj więcej cytatów ™ . Jestem pewien, że @CoverosGene miał dobre intencje, ale to po prostu okropne.
ls
jest ls -1 | while read line; do stuff; done
. Przynajmniej ten nie złamie się dla białych znaków.
mv -v
tobą nie potrzebujeszecho "moved $x -> $t"
Korzystanie z danych wyjściowych w ls
celu uzyskania nazw plików jest złym pomysłem . Może to prowadzić do nieprawidłowego działania, a nawet niebezpiecznych skryptów. To dlatego, że nazwa pliku może zawierać dowolny znak oprócz /
i null
charakter, a ls
nie korzysta z żadnego z tych znaków jako ograniczniki, więc jeśli nazwa pliku zawiera spację lub znak nowej linii, to będzie otrzymać nieoczekiwane rezultaty.
Istnieją dwa bardzo dobre sposoby iteracji plików. Tutaj zwykłem echo
demonstrować robienie czegoś z nazwą pliku; możesz jednak użyć wszystkiego.
Pierwszym z nich jest użycie natywnych funkcji globowania powłoki.
for dir in */; do
echo "$dir"
done
Powłoka rozwija się */
w osobne argumenty for
odczytywane przez pętlę; nawet jeśli w nazwie pliku znajduje się spacja, znak nowej linii lub jakikolwiek inny dziwny znak, for
każda pełna nazwa będzie postrzegana jako jednostka atomowa; w żaden sposób nie analizuje listy.
Jeśli chcesz przechodzić rekurencyjnie do podkatalogów, nie będzie to możliwe, chyba że twoja powłoka ma jakieś rozszerzone funkcje globowania (takie jak bash
s globstar
. w różnych systemach, następną opcją jest użycie find
.
find . -type d -exec echo '{}' \;
Tutaj find
polecenie wywoła echo
i przekaże mu argument nazwy pliku. Robi to raz dla każdego znalezionego pliku. Podobnie jak w poprzednim przykładzie, nie ma parsowania listy nazw plików; zamiast tego nazwa pliku jest wysyłana całkowicie jako argument.
Składnia -exec
argumentu wygląda trochę zabawnie. find
pobiera pierwszy argument za -exec
i traktuje go jako program do uruchomienia, a każdy kolejny argument przyjmuje jako argument do przekazania do tego programu. Są dwa specjalne argumenty, które -exec
należy zobaczyć. Pierwszy to {}
; ten argument zostaje zastąpiony nazwą pliku, którą find
generują poprzednie części . Drugi to ;
, który informuje find
, że jest to koniec listy argumentów przekazywanych do programu; find
potrzebuje tego, ponieważ możesz kontynuować z większą liczbą argumentów, które są przeznaczone find
i nie są przeznaczone dla wykonywanego programu. Powodem \
jest to, że powłoka również traktuje;
szczególnie - reprezentuje koniec polecenia, więc musimy uciec od niego, aby powłoka podała go jako argument find
zamiast konsumować go dla siebie; innym sposobem, aby powłoka nie traktowała go specjalnie, jest umieszczenie go w cudzysłowach: ';'
działa tak dobrze, jak \;
w tym celu.
find
. Istnieje magia, którą można zrobić, a nawet postrzegane ograniczenia -exec
można obejść. -print0
Opcją jest także cenne dla użycia z xargs
.
W przypadku plików ze spacjami musisz podać zmienną:
for i in $(ls); do echo "$i"; done;
lub możesz zmienić zmienną środowiskową separatora pól wejściowych (IFS):
IFS=$'\n';for file in $(ls); do echo $i; done
Wreszcie, w zależności od tego, co robisz, możesz nawet nie potrzebować ls:
for i in *; do echo "$i"; done;
\n
jest niewystarczające. Nigdy nie jest dobrym pomysłem zalecanie analizy wyniku ls
.
Jeśli masz zainstalowany GNU Parallel http://www.gnu.org/software/parallel/, możesz to zrobić:
ls | parallel echo {} is in this dir
Aby zmienić nazwę wszystkich plików .txt na .xml:
ls *.txt | parallel mv {} {.}.xml
Obejrzyj wideo wprowadzające do GNU Parallel, aby dowiedzieć się więcej: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Dlaczego nie ustawić IFS na nowy wiersz, a następnie przechwycić dane wyjściowe ls
w tablicy? Ustawienie IFS na nowy wiersz powinno rozwiązać problemy ze śmiesznymi znakami w nazwach plików; używanie ls
może być przyjemne, ponieważ ma wbudowaną funkcję sortowania.
(Podczas testowania miałem problem z ustawieniem IFS na, \n
ale ustawienie na backspace nowej linii działa, jak sugerowano gdzie indziej tutaj):
Np. (Zakładając, że ls
przekazano pożądany wzorzec wyszukiwania $1
):
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
FILES=($(/bin/ls "$1"))
for AFILE in ${FILES[@]}
do
... do something with a file ...
done
IFS=$SAVEIFS
Jest to szczególnie przydatne w systemie OS X, np. Aby przechwycić listę plików posortowanych według daty utworzenia (od najstarszej do najnowszej), ls
polecenie to ls -t -U -r
.
\n
jest niewystarczające. Jedyne prawidłowe rozwiązania używają pętli for z rozszerzaniem nazw ścieżek lub find
.
Tak to robię, ale są prawdopodobnie bardziej wydajne sposoby.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done