Например, сейчас я использую следующее, чтобы изменить пару файлов, чьи пути Unix я записал в файл:
cat file.txt | while read in; do chmod 755 "$in"; done
Есть ли более элегантный, безопасный способ?
Это потому, что есть не только 1 ответ ...
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/
Примечание: я оставил зачеркнутую версию, потому что этот синтаксис может быть полезен при выполнении многих операций ввода-вывода с процессом Parallels:
mkfifo sshfifo
exec 7> >(ssh -t user@host sh >sshfifo)
exec 6<sshfifo
xargs
было initialy билд для ответа на этот вид потребности, некоторые функции, такие как команды построения как можно дольше в текущих условиях для вызоваchmod
в этом случае как можно меньше, уменьшая вилки обеспечивают Efficience.while ;do..done <$file
подразумевается запуск 1 форка на 1 файл.xargs
мог запустить 1 форк для тысячи файлов ... надежным способом.cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
tr \\n \\0 <file.txt |xargs -0 [command]
примерно на 50% быстрее, чем метод, который вы описали.Да.
Таким образом, вы можете избежать
cat
процесса.cat
почти всегда плохо для такой цели, как эта. Вы можете прочитать больше о Бесполезном Использовании Кошки.источник
cat
хорошая идея, но в этом случае, указанная командаxargs
chmod
(то есть действительно выполнить одну команду для каждой строки в файле).read -r
читает одну строку из стандартного ввода (read
без-r
интерпретации обратной косой черты, вы этого не хотите)».если у вас есть хороший селектор (например, все .txt файлы в директории), вы можете сделать:
удар за петлю
или ваш вариант:
источник
Если вы знаете, что у вас нет пробелов на входе:
Если в путях могут быть пробелы, и если у вас есть GNU xargs:
источник
xargs
это надежно . Этот инструмент очень старый и его код сильно пересмотрен . Его целью было изначально построить линии с учетом ограничений оболочки (64kchar / line или что-то в этом роде). Теперь этот инструмент может работать с очень большими файлами и может значительно уменьшить количество разветвлений до финальной команды. Смотрите мой ответ и / илиman xargs
.brew install findutils
), а затемgxargs
вместо этого вызывать GNU xargs , напримерgxargs chmod 755 < file.txt
Если вы хотите запустить свою команду параллельно для каждой строки, вы можете использовать GNU Parallel
Каждая строка вашего файла будет передана программе в качестве аргумента. По умолчанию
parallel
выполняется столько потоков, сколько подсчитано для ваших процессоров. Но вы можете указать это с-j
источник
Я вижу, что вы пометили bash, но Perl также был бы хорошим способом сделать это:
Вы также можете применить регулярное выражение, чтобы убедиться, что вы получаете нужные файлы, например, для обработки только .txt файлов:
Чтобы «просмотреть» происходящее, просто замените галочки на двойные кавычки и добавьте
print
:источник
chmod
функциюperl -lpe 'chmod 0755, $_' file.txt
- использовать-l
для функции "авто-chomp"Вы также можете использовать AWK, что даст вам больше возможностей для обработки файла.
если в вашем файле есть разделитель полей, например:
Чтобы получить только первое поле, которое вы делаете
Вы можете проверить более подробную информацию в документации GNU https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
источник
Логика относится ко многим другим целям. А как читать .sh_history каждого пользователя из / home / filesystem? Что делать, если их тысячи?
Вот скрипт https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
источник
Я знаю, что уже поздно, но все же
Если по какой-либо причине вы столкнетесь с сохраненным в Windows текстовым файлом
\r\n
вместо\n
, вы можете запутаться в результате, если в качестве аргумента в вашей команде указан sth после строки чтения. Так что удалите\r
, например:источник