Czy ktoś ma skrypt powłoki szablonów do robienia czegoś z lslistą 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 lslistą 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..donepętli:
for f in *; do
echo "File -> $f"
done
Można wymienić *z *.txtlub 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 dopętli po prostu odwołujesz się do zmiennej pętli z prefiksem znaku dolara (tak $fw powyższym przykładzie). Możesz echoto zrobić lub zrobić cokolwiek innego, co chcesz.
Na przykład, aby zmienić nazwę wszystkich .xmlplikó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
forpę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.
lsdanych wyjściowych , nie powinieneś czytać danych wyjściowych w forpętli , powinieneś użyć $()zamiast `` i Użyj więcej cytatów ™ . Jestem pewien, że @CoverosGene miał dobre intencje, ale to po prostu okropne.
lsjest ls -1 | while read line; do stuff; done. Przynajmniej ten nie złamie się dla białych znaków.
mv -vtobą nie potrzebujeszecho "moved $x -> $t"
Korzystanie z danych wyjściowych w lscelu 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 nullcharakter, a lsnie 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 echodemonstrować 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 forodczytywane przez pętlę; nawet jeśli w nazwie pliku znajduje się spacja, znak nowej linii lub jakikolwiek inny dziwny znak, forkaż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 bashs globstar. w różnych systemach, następną opcją jest użycie find.
find . -type d -exec echo '{}' \;
Tutaj findpolecenie wywoła echoi 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 -execargumentu wygląda trochę zabawnie. findpobiera pierwszy argument za -execi 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 -execnależy zobaczyć. Pierwszy to {}; ten argument zostaje zastąpiony nazwą pliku, którą findgenerują poprzednie części . Drugi to ;, który informuje find, że jest to koniec listy argumentów przekazywanych do programu; findpotrzebuje tego, ponieważ możesz kontynuować z większą liczbą argumentów, które są przeznaczone findi 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 findzamiast 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 -execmożna obejść. -print0Opcją 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;
\njest 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 lsw tablicy? Ustawienie IFS na nowy wiersz powinno rozwiązać problemy ze śmiesznymi znakami w nazwach plików; używanie lsmoże być przyjemne, ponieważ ma wbudowaną funkcję sortowania.
(Podczas testowania miałem problem z ustawieniem IFS na, \nale ustawienie na backspace nowej linii działa, jak sugerowano gdzie indziej tutaj):
Np. (Zakładając, że lsprzekazano 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), lspolecenie to ls -t -U -r.
\njest 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