У когось є сценарій оболонки шаблону для того, щоб щось робити зі ls
списком імен каталогів та перебирати їх через кожне і щось робити?
Я планую зробити, ls -1d */
щоб отримати список імен каталогів.
У когось є сценарій оболонки шаблону для того, щоб щось робити зі ls
списком імен каталогів та перебирати їх через кожне і щось робити?
Я планую зробити, ls -1d */
щоб отримати список імен каталогів.
Відповіді:
Відредаговано, щоб не використовувати ls там, де робиться глобус, як @ shawn-j-goff та інші запропонували.
Просто використовуйте for..do..done
цикл:
for f in *; do
echo "File -> $f"
done
Ви можете замінити *
з *.txt
будь-якої іншої Glob , який повертає список (файлів, каталогів або що - небудь в цьому відношенні), команда , яка генерує список, наприклад, $(cat filelist.txt)
або на самому ділі замінити його в списку.
У do
циклі ви просто посилаєтесь на змінну циклу з префіксом знака долара (так $f
у наведеному вище прикладі). Ви можете echo
це зробити або зробити все, що завгодно.
Наприклад, перейменувати всі .xml
файли в поточному каталозі на .txt
:
for x in *.xml; do
t=$(echo $x | sed 's/\.xml$/.txt/');
mv $x $t && echo "moved $x -> $t"
done
Або ще краще, якщо ви використовуєте Bash, ви можете використовувати розширення параметрів Bash, а не нерестування нижньої оболонки:
for x in *.xml; do
t=${x%.xml}.txt
mv $x $t && echo "moved $x -> $t"
done
for
циклів і типовий підводне спробу розбору результатів ls
. @ DanielA.White, ви могли б розглянути unaccepting ця відповідь , якщо він не був корисним (або може ввести в оману), так як ви сказали, ви дієте на каталоги. Відповідь Шона Дж. Гоффа має забезпечити більш надійне та дієве рішення вашої проблеми.
ls
вихід , ви не повинні читати вихід у for
циклі , ви повинні використовувати $()
замість `` та використовувати більше котирувань ™ . Я впевнений, що @CoverosGene мав на увазі добре, але це просто жахливо.
ls
є ls -1 | while read line; do stuff; done
. Принаймні, той не відіб’ється на пробіли.
mv -v
тобою не потрібноecho "moved $x -> $t"
Використання результатів ls
для отримання імен файлів - погана ідея . Це може призвести до несправності та навіть небезпечних сценаріїв. Це відбувається тому , що ім'я файлу може містити будь-які символи , крім /
і null
характер, і ls
не використовувати один з цих символів в якості роздільників, так що якщо ім'я файлу містить пробіл або символ нового рядка, ви будете отримувати несподівані результати.
Є два дуже хороших способи ітерації файлів. Тут я просто echo
демонстрував, що роблю щось із назвою файлу; ви можете використовувати все, що завгодно.
Перший полягає у використанні натільних функцій оболонки оболонки.
for dir in */; do
echo "$dir"
done
Оболонка розширюється */
на окремі аргументи, які for
читає цикл; навіть якщо у назві файлу є пробіл, новий рядок або будь-який інший дивний символ, for
кожне повне ім’я буде бачитись як атомна одиниця; це ніяк не розбирає список.
Якщо ви хочете рекурсивно переходити до підкаталогів, це не буде робити, якщо ваша оболонка не має деяких розширених функцій глобалізації (наприклад, bash
s globstar
. Якщо ваша оболонка не має цих можливостей, або якщо ви хочете переконатися, що ваш сценарій буде працювати у різних системах, то наступним варіантом є використання find
.
find . -type d -exec echo '{}' \;
Тут find
команда викличе echo
та передасть їй аргумент імені файлу. Це робиться один раз для кожного знайденого файлу. Як і в попередньому прикладі, тут немає розбору списку імен файлів; натомість ім'я файлу надсилається повністю як аргумент.
Синтаксис -exec
аргументу виглядає дещо смішним. find
приймає перший аргумент після -exec
і трактує, що як програма, що запускається, так і кожен наступний аргумент, він береться як аргумент для переходу до цієї програми. Є два особливих аргументи, які -exec
потрібно побачити. Перший - це {}
; цей аргумент замінюється на ім'я файлу, яке find
створюються попередніми частинами . Другий - це означає ;
, що find
це кінець списку аргументів, які потрібно передавати програмі; find
це потрібно, тому що ви можете продовжити більше аргументів, призначених find
і не призначених для програми exec'd. Причиною цього \
є те, що шкаралупу також лікують;
спеціально - це являє собою кінець команди, тому нам потрібно уникнути цього, щоб оболонка давала це як аргумент, find
а не споживає його для себе; Ще один спосіб отримати оболонку, щоб не ставитися до неї спеціально, - це помістити її в лапки: ';'
працює так само добре, як \;
для цієї мети.
find
. Є магія, яку можна зробити, і навіть сприйняті обмеження -exec
можна обійти. Цей -print0
варіант також цінний для використання з xargs
.
Для файлів з пробілами у вас обов’язково потрібно переконатись у цитуванні змінної на зразок:
for i in $(ls); do echo "$i"; done;
або, ви можете змінити змінну середовища розділення вводу (IFS):
IFS=$'\n';for file in $(ls); do echo $i; done
Нарешті, залежно від того, що ви робите, можливо, вам навіть не знадобиться ls:
for i in *; do echo "$i"; done;
\n
недостатньо. Ніколи не рекомендується рекомендувати розбір результатів ls
.
Якщо у вас встановлений GNU Parallel http://www.gnu.org/software/parallel/, ви можете це зробити:
ls | parallel echo {} is in this dir
Щоб перейменувати всі .txt у .xml:
ls *.txt | parallel mv {} {.}.xml
Перегляньте вступне відео для GNU Parallel, щоб дізнатися більше: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Чому б не встановити IFS на новий рядок, а потім захопити висновок ls
масиву? Встановлення IFS у новий рядок повинно вирішувати проблеми із забавними символами у назвах файлів; використання ls
може бути приємним, оскільки у нього є вбудована система сортування.
(Під час тестування у мене виникли проблеми з налаштуванням IFS, \n
але налаштування його на нову лінію працює, як це запропоновано в іншому місці):
Напр. (За умови, що бажаний ls
шаблон пошуку передано $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
Це особливо зручно в OS X, наприклад, для захоплення списку файлів, відсортованих за датою створення (найдавнішою до новітньої), ls
команда є ls -t -U -r
.
\n
недостатньо. Єдині дійсні рішення використовують цикл for з розширенням імені шляху, або find
.
Так я це роблю, але, мабуть, є більш ефективні способи.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done