Відповіді:
Це тому, що існує не одна відповідь ...
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<sshfifocat 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 <$fileimplie працює 1 виделкою на 1 файл.xargsміг би запустити 1 вилку для тисячі файлів ... надійно.