Respostas:
Isso ocorre porque não há apenas 1 resposta ...
shell expansão da linha de comandoxargs ferramenta dedicadawhile read com algumas observaçõeswhile read -uusando dedicado fd, para processamento interativo (amostra)Quanto ao pedido OP: correndo chmodem todos os alvos listados no arquivo , xargsé a ferramenta indicada. Mas para algumas outras aplicações, pequena quantidade de arquivos, etc ...
Se o seu arquivo não for muito grande e todos os arquivos forem bem nomeados (sem espaços ou outros caracteres especiais, como aspas), você poderá usar a shellexpansão da linha de comando . Simplesmente:
chmod 755 $(<file.txt)
Para pequena quantidade de arquivos (linhas), este comando é o mais leve.
xargs é a ferramenta certaPara uma quantidade maior de arquivos ou quase qualquer número de linhas no seu arquivo de entrada ...
Para muitos binutils ferramentas, como chown, chmod, rm, cp -t...
xargs chmod 755 <file.txt
Se você possui caracteres especiais e / ou várias linhas file.txt.
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
se seu comando precisar ser executado exatamente 1 vez por entrada:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
Isso não é necessário para esta amostra, pois chmodaceita vários arquivos como argumento, mas corresponde ao título da pergunta.
Para algum caso especial, você pode até definir o local do argumento do arquivo nos comandos gerados por xargs:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5como entradaTente o seguinte:
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..
Onde commande é feito uma vez por linha .
while read e variantes.Como o OP sugere, cat file.txt | while read in; do chmod 755 "$in"; doneele funcionará, mas existem 2 problemas:
cat |é um garfo inútil e
| while ... ;donese tornará um subshell onde o ambiente desaparecerá depois ;done.
Portanto, isso poderia ser melhor escrito:
while read in; do chmod 755 "$in"; done < file.txt
Mas,
Você pode ser avisado sobre $IFSe readsinalizadores:
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...
Em alguns casos, pode ser necessário usar
while IFS= read -r in;do chmod 755 "$in";done <file.txt
Para evitar problemas com nomes de arquivos estranhos. E talvez se você encontrar problemas comUTF-8 :
while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txtEnquanto você usa STDINpara ler file.txt, seu script não pode ser interativo (você não pode STDINmais usar ).
while read -u, usando dedicado fd .Sintaxe: while read ...;done <file.txtredirecionaráSTDIN para file.txt. Isso significa que você não poderá lidar com o processo até que ele termine.
Se você planeja criar uma ferramenta interativa , evite o uso deSTDIN e usar algum descritor de arquivo alternativo .
Constantes descritores de arquivos são: 0para STDIN , 1para STDOUT e 2para STDERR . Você poderia vê-los por:
ls -l /dev/fd/
ou
ls -l /proc/self/fd/
A partir daí, você deve escolher um número não utilizado, entre 0e 63(mais, de fato, dependendo dasysctl ferramenta de superusuário) como descritor de arquivo :
Para esta demonstração, usarei o fd 7 :
exec 7<file.txt # Without spaces between `7` and `<`!
ls -l /dev/fd/
Então você pode usar read -u 7desta maneira:
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
Para fechar fd/7:
exec 7<&- # This will close file descriptor 7.
ls -l /dev/fd/
Nota: Eu deixei a versão marcada, porque essa sintaxe pode ser útil, ao fazer muitas E / S com processo paralelo:
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]é cerca de 50% mais rápido que o método que você descreveu.
Sim.
while read in; do chmod 755 "$in"; done < file.txt
Dessa forma, você pode evitar um catprocesso.
catquase sempre é ruim para um propósito como esse. Você pode ler mais sobre o uso inútil do gato.
cat seja uma boa ideia, mas, neste caso, o comando indicado éxargs
chmod(ou seja, realmente executar um comando para cada linha do arquivo).
read -rlê uma única linha da entrada padrão ( readsem -rinterpretar barras invertidas, você não deseja isso)."
se você tiver um bom seletor (por exemplo, todos os arquivos .txt em um diretório), poderá:
for i in *.txt; do chmod 755 "$i"; done
ou uma variante sua:
while read line; do chmod 755 "$line"; done <file.txt
Se você sabe que não possui nenhum espaço em branco na entrada:
xargs chmod 755 < file.txt
Se houver espaço em branco nos caminhos, e se você tiver GNU xargs:
tr '\n' '\0' < file.txt | xargs -0 chmod 755
xargsé robusto. Essa ferramenta é muito antiga e seu código é fortemente revisitado . Seu objetivo era inicialmente criar linhas em relação às limitações do shell (64kchar / line ou algo assim). Agora, essa ferramenta pode funcionar com arquivos muito grandes e pode reduzir muito o número de bifurcações para o comando final. Vejo minha resposta e / ou man xargs.
brew install findutils ) e, em seguida, invocar o GNU xargs com gxargs, por exemplo, por exemplo:gxargs chmod 755 < file.txt
Se você deseja executar seu comando em paralelo para cada linha, você pode usar o GNU Parallel
parallel -a <your file> <program>
Cada linha do seu arquivo será passada para o programa como argumento. Por padrão, parallelexecuta tantos threads quanto suas CPUs contam. Mas você pode especificá-lo com-j
Vejo que você marcou o bash, mas o Perl também seria uma boa maneira de fazer isso:
perl -p -e '`chmod 755 $_`' file.txt
Você também pode aplicar uma regex para garantir que está obtendo os arquivos corretos, por exemplo, para processar apenas os arquivos .txt:
perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt
Para "pré-visualizar" o que está acontecendo, substitua os backticks por aspas duplas e preencha print:
perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt
chmodfunção
perl -lpe 'chmod 0755, $_' file.txt- use -lpara o recurso "auto-chomp"
Você também pode usar o AWK, que oferece mais flexibilidade para lidar com o arquivo
awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt
se o seu arquivo tiver um separador de campos como:
campo1, campo2, campo3
Para obter apenas o primeiro campo que você faz
awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt
Você pode verificar mais detalhes na documentação do GNU https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
A lógica se aplica a muitos outros objetivos. E como ler .sh_history de cada usuário em / home / filesystem? E se houver milhares deles?
#!/bin/ksh
last |head -10|awk '{print $1}'|
while IFS= read -r line
do
su - "$line" -c 'tail .sh_history'
done
Aqui está o script https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
Eu sei que é tarde, mas ainda
Se por acaso você encontrar o arquivo de texto salvo no Windows em \r\nvez de \n, poderá ficar confuso com a saída se o seu comando sth após a linha de leitura como argumento. Então remova \r, por exemplo:
cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
xargsfoi criado inicialmente para responder a esse tipo de necessidade, alguns recursos, como criar o comando o maior tempo possível no ambiente atual, para invocarchmodneste caso o menos possível, reduzir os garfos garante a eficiência.while ;do..done <$fileimplie executando 1 fork para 1 arquivo.xargspoderia executar 1 fork para milhares de arquivos ... de maneira confiável.