Відповіді:
Це тому, що існує не одна відповідь ...
shell
розширення командного рядкаxargs
виділений інструментwhile read
з деякими зауваженнямиwhile read -u
використання спеціальної fd
, для інтерактивної обробки (зразок)Що стосується запиту OP: працюєш chmod
на всіх цілях , перерахованих у файлі , xargs
є зазначеним інструментом. Але для деяких інших програм невелика кількість файлів тощо ...
Якщо ваш файл не надто великий і всі файли названі добре (без пробілів чи інших спеціальних символів, таких як лапки), ви можете використовувати shell
розширення командного рядка . Просто:
chmod 755 $(<file.txt)
Для невеликої кількості файлів (рядків) ця команда є легшою.
xargs
є правильним інструментомДля більшої кількості файлів або майже будь-якої кількості рядків у вхідному файлі ...
Для багатьох BinUtils інструментів, як chown
, chmod
, rm
, cp -t
...
xargs chmod 755 <file.txt
Якщо у вас є спеціальні символи та / або багато рядків file.txt
.
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
якщо вашу команду потрібно запустити рівно 1 раз за записом:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
Це не потрібно для цього зразка, оскільки chmod
приймайте декілька файлів як аргумент, але це відповідає заголовку питання.
Для якогось особливого випадку ви навіть можете визначити розташування аргументу файлу в командах, згенерованих xargs
:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5
введеннямСпробуйте це:
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
Де команда робиться один раз у рядку .
while read
і варіанти.Як пропонують ОП, cat file.txt | while read in; do chmod 755 "$in"; done
буде працювати, але є 2 питання:
cat |
є марною виделкою , і
| while ... ;done
перетвориться на допоміжну оболонку, де після цього середовище не буде працювати ;done
.
Так що можна було б краще написати:
while read in; do chmod 755 "$in"; done < file.txt
Але,
Вас можуть попередити $IFS
і read
прапори:
help read
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
У деяких випадках вам може знадобитися використання
while IFS= read -r in;do chmod 755 "$in";done <file.txt
Для уникнення проблем з іменами прізвищ. І, можливо, якщо у вас виникнуть проблеми з UTF-8
:
while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
Хоча ви використовуєте STDIN
для читання file.txt
, ваш сценарій не міг бути інтерактивним (ви більше не можете його використовувати STDIN
).
while read -u
, використовуючи виділені fd
.Синтаксис: while read ...;done <file.txt
буде переспрямовано STDIN
на file.txt
. Це означає, що ви не зможете впоратися з процесом, поки вони не закінчаться.
Якщо ви плануєте створити інтерактивний інструмент, вам слід уникати використання STDIN
та використання альтернативного дескриптора файлів .
Дескриптори файлів констант : 0
для STDIN , 1
для STDOUT та 2
для STDERR . Ви можете побачити їх:
ls -l /dev/fd/
або
ls -l /proc/self/fd/
Звідти ви повинні вибрати невикористане число, між 0
та 63
(більше, насправді, залежно від sysctl
інструмента суперрузера) як дескриптор файлу :
Для цієї демонстрації я буду використовувати fd 7
:
exec 7<file.txt # Without spaces between `7` and `<`!
ls -l /dev/fd/
Тоді ви можете використовувати read -u 7
такий спосіб:
while read -u 7 filename;do
ans=;while [ -z "$ans" ];do
read -p "Process file '$filename' (y/n)? " -sn1 foo
[ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
done
if [ "$ans" = "y" ] ;then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
done
Щоб закрити fd/7
:
exec 7<&- # This will close file descriptor 7.
ls -l /dev/fd/
Нота: Я пускаю вражену версію, оскільки цей синтаксис може бути корисним, коли ви робите багато вводу-виводу з паралельними процесами:
mkfifo sshfifo
exec 7> >(ssh -t user@host sh >sshfifo)
exec 6<sshfifo
cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
tr \\n \\0 <file.txt |xargs -0 [command]
приблизно на 50% швидше, ніж описаний вами метод.
Так.
while read in; do chmod 755 "$in"; done < file.txt
Таким чином ви можете уникнути cat
процесу.
cat
майже завжди поганий для такої мети, як ця. Ви можете прочитати більше про марне використання кота.
cat
хороша ідея, але в цьому випадку, зазначена командаxargs
chmod
(тобто реально виконати одну команду для кожного рядка у файлі).
read -r
читає один рядок зі стандартного вводу ( read
без -r
інтерпретації косої риски, цього не потрібно)."
якщо у вас є хороший селектор (наприклад, усі .txt файли в режимі), ви можете зробити:
for i in *.txt; do chmod 755 "$i"; done
або ваш варіант:
while read line; do chmod 755 "$line"; done <file.txt
Якщо ви знаєте, що у вводі немає жодного пробілу:
xargs chmod 755 < file.txt
Якщо в шляхах може бути пробіл і якщо у вас є xargs GNU:
tr '\n' '\0' < file.txt | xargs -0 chmod 755
xargs
надійний. Цей інструмент дуже старий, і його код сильно переглядається . Його метою було спочатку створити лінії щодо обмежень оболонок (64 ккал / лінія чи щось таке). Тепер цей інструмент може працювати з дуже великими файлами і може значно зменшити кількість форк до остаточної команди. Дивіться мою відповідь та / або man xargs
.
brew install findutils
), а потім викликати xargs GNU за допомогою gxargs
, наприкладgxargs chmod 755 < file.txt
Якщо ви хочете виконувати свою команду паралельно для кожного рядка, ви можете використовувати паралель GNU
parallel -a <your file> <program>
Кожен рядок вашого файлу буде переданий програмі як аргумент. За замовчуванням parallel
працює стільки потоків, скільки ваших процесорів. Але ви можете вказати це за допомогою-j
Я бачу, що ви помітили баш, але Perl також був би хорошим способом зробити це:
perl -p -e '`chmod 755 $_`' file.txt
Ви також можете застосувати регулярний вираз, щоб переконатися, що ви отримуєте потрібні файли, наприклад, лише для обробки файлів .txt:
perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt
Щоб "переглянути" те, що відбувається, просто замініть основу подвійними лапками і додайте print
:
perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt
chmod
функція
perl -lpe 'chmod 0755, $_' file.txt
- використовуйте -l
для функції "auto-chomp"
Ви також можете скористатись AWK, що надасть вам більше гнучкості в обробці файлу
awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt
якщо у вашому файлі є роздільник полів:
поле1, поле2, поле3
Щоб отримати лише перше поле, яке ви виконуєте
awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt
Ви можете ознайомитись з додатковою інформацією на GNU Documentation https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
Логіка стосується багатьох інших цілей. І як читати .sh_history кожного користувача з / home / файлової системи? Що робити, якщо їх тисяча?
#!/bin/ksh
last |head -10|awk '{print $1}'|
while IFS= read -r line
do
su - "$line" -c 'tail .sh_history'
done
Ось сценарій https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
Я знаю, що пізно, але все ж
Якщо ви випадково наштовхнетесь на збережений текстовий файл Windows \r\n
замість \n
, ви можете заплутатися у виводі, якщо у вашій команді буде аргументовано sth після прочитаного рядка як аргумент. Отже, видаліть \r
, наприклад:
cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
xargs
було побудовано для відповіді на подібні потреби, деякі функції, такі як команда побудови якомога довше в поточному середовищі, щоб викликатиchmod
в цьому випадку якомога менше, зменшуючи вилки, забезпечують ефективність.while ;do..done <$file
implie працює 1 виделкою на 1 файл.xargs
міг би запустити 1 вилку для тисячі файлів ... надійно.