Digamos, eu tenho um arquivo foo.txt
especificando N
argumentos
arg1
arg2
...
argN
que eu preciso passar para o comando my_command
Como uso as linhas de um arquivo como argumentos de um comando?
Digamos, eu tenho um arquivo foo.txt
especificando N
argumentos
arg1
arg2
...
argN
que eu preciso passar para o comando my_command
Como uso as linhas de um arquivo como argumentos de um comando?
Respostas:
Se seu shell é bash (entre outros), um atalho para $(cat afile)
é $(< afile)
, então você deve escrever:
mycommand "$(< file.txt)"
Documentado na página de manual do bash na seção 'Substituição de comando'.
Em alternativa, leia seu comando a partir de stdin, portanto: mycommand < file.txt
<
operador. Isso significa que o próprio shell executará o redirecionamento em vez de executar o cat
binário por suas propriedades de redirecionamento.
"$(…)"
, sem , o conteúdo de file.txt
seria passado para a entrada padrão de mycommand
, não como argumento. "$(…)"
significa executar o comando e devolver a saída como uma string ; aqui o "comando" lê apenas o arquivo, mas poderia ser mais complexo.
Como já mencionado, você pode usar os backticks ou $(cat filename)
.
O que não foi mencionado, e acho importante notar, é que você deve se lembrar que o shell separará o conteúdo desse arquivo de acordo com o espaço em branco, fornecendo cada "palavra" encontrada ao seu comando como argumento. E embora você possa incluir um argumento da linha de comando entre aspas, para que ele possa conter espaços em branco, seqüências de escape etc., a leitura do arquivo não fará a mesma coisa. Por exemplo, se o seu arquivo contiver:
a "b c" d
os argumentos que você receberá são:
a
"b
c"
d
Se você deseja extrair cada linha como argumento, use a construção while / read / do:
while read i ; do command_name $i ; done < filename
read -r
menos que você queira expandir as seqüências de escape de barra invertida - e NUL é um delimitador mais seguro para usar do que a nova linha, principalmente se os argumentos que você está passando são coisas como nomes de arquivos, que podem conter novas linhas literais. Além disso, sem limpar o IFS, você obtém o espaço em branco inicial e final implicitamente limpo i
.
command `< file`
passará o conteúdo do arquivo para o comando stdin, mas removerá as novas linhas, o que significa que você não pode repetir cada linha individualmente. Para isso, você pode escrever um script com um loop 'for':
for line in `cat input_file`; do some_command "$line"; done
Ou (a variante de várias linhas):
for line in `cat input_file`
do
some_command "$line"
done
Ou (variante de várias linhas com em $()
vez de ``
):
for line in $(cat input_file)
do
some_command "$line"
done
< file
backticks significa que ele realmente não executa um redirecionamento command
.
command `< file`
Não funcionam no bash ou no POSIX sh; zsh é uma questão diferente, mas esse não é o shell sobre o qual esta questão se refere).
Hello World
em uma linha. Você verá a primeira execução some_command Hello
, então some_command World
, não some_command "Hello World"
ou some_command Hello World
.
Você faz isso usando backticks:
echo World > file.txt
echo Hello `cat file.txt`
echo "Hello * Starry * World" > file.txt
na primeira etapa, obteria pelo menos quatro argumentos separados passados para o segundo comando - e provavelmente mais, como os *
s se expandem para os nomes dos arquivos presentes no diretório atual.
fork()
desabilitando um subshell com um FIFO anexado ao seu stdout, invocando /bin/cat
como filho desse subshell e lendo a saída através do FIFO; compare to $(<file.txt)
, que lê o conteúdo do arquivo no bash diretamente, sem subshells ou FIFOs envolvidos.
Se você deseja fazer isso de uma maneira robusta que funcione para todos os argumentos de linha de comando possíveis (valores com espaços, valores com novas linhas, valores com caracteres de aspas literais, valores não imprimíveis, valores com caracteres globais, etc.), fica um pouco mais interessante.
Para gravar em um arquivo, com uma matriz de argumentos:
printf '%s\0' "${arguments[@]}" >file
... substituir com "argument one"
, "argument two"
etc., conforme apropriado.
Para ler esse arquivo e usar seu conteúdo (no bash, ksh93 ou em outro shell recente com matrizes):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
Para ler esse arquivo e usar seu conteúdo (em um shell sem matrizes; observe que isso substituirá sua lista de argumentos da linha de comando local e, portanto, é melhor executá-lo dentro de uma função, de modo que você substitua os argumentos da função e não a lista global):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Observe que -d
(permitir que um delimitador de final de linha diferente seja usado) é uma extensão não POSIX, e um shell sem matrizes também pode não suportá-lo. Nesse caso, pode ser necessário usar uma linguagem não shell para transformar o conteúdo delimitado por NUL em um eval
formato seguro:
quoted_list() {
## Works with either Python 2.x or 3.x
python -c '
import sys, pipes, shlex
quote = pipes.quote if hasattr(pipes, "quote") else shlex.quote
print(" ".join([quote(s) for s in sys.stdin.read().split("\0")][:-1]))
'
}
eval "set -- $(quoted_list <file)"
run_your_command "$@"
Aqui está como passo o conteúdo de um arquivo como argumento para um comando:
./foo --bar "$(cat ./bar.txt)"
$(<bar.txt)
(ao usar um shell, como o bash, com a extensão apropriada). $(<foo)
é um caso especial: diferentemente da substituição regular de comandos, como usada $(cat ...)
, ela não utiliza um subshell para operar e, assim, evita uma grande sobrecarga.
Se tudo o que você precisa fazer é transformar o arquivo arguments.txt
com o conteúdo
arg1
arg2
argN
para my_command arg1 arg2 argN
então você pode simplesmente usar xargs
:
xargs -a arguments.txt my_command
Você pode colocar argumentos estáticos adicionais na xargs
chamada, como o xargs -a arguments.txt my_command staticArg
que chamarámy_command staticArg arg1 arg2 argN
No meu shell bash, o seguinte funcionou como um encanto:
cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'
onde input_file é
arg1
arg2
arg3
Como é evidente, isso permite que você execute vários comandos com cada linha de input_file, um pequeno truque que aprendi aqui .
$(somecommand)
, esse comando será executado em vez de ser transmitido como texto. Da mesma forma, >/etc/passwd
será processado como um redirecionamento e substituição /etc/passwd
(se executado com permissões apropriadas) etc.
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
- e também mais eficiente, pois passa o maior número possível de argumentos para cada shell possível, em vez de iniciar um shell por linha no seu arquivo de entrada.
cat
Depois de editar a resposta de @Wesley Rice algumas vezes, decidi que minhas alterações estavam ficando grandes demais para continuar alterando sua resposta em vez de escrever a minha. Então, eu decidi que preciso escrever o meu!
Leia cada linha de um arquivo e opere linha por linha assim:
#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
Isto vem diretamente do autor Vivek Gite aqui: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . Ele recebe o crédito!
Sintaxe: Leia o arquivo linha por linha em um shell Bash Unix e Linux:
1. A sintaxe é a seguinte para bash, ksh, zsh e todos os outros shells para ler um arquivo linha por linha
2.while read -r line; do COMMAND; done < input.file
3. A-r
opção passou para o comando read impede que as fugas de barra invertida sejam interpretadas.
4. Adicione aIFS=
opção antes do comando de leitura para impedir que os espaços em branco à esquerda / à direita sejam aparados -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file
E agora, para responder a essa pergunta agora fechada, que eu também tinha: É possível `git add` uma lista de arquivos de um arquivo?- aqui está a minha resposta:
Observe que FILES_STAGED
é uma variável que contém o caminho absoluto para um arquivo que contém várias linhas em que cada linha é um caminho relativo para um arquivo que eu gostaria de fazer git add
. Este trecho de código está prestes a se tornar parte do arquivo "eRCaGuy_dotfiles / useful_scripts / sync_git_repo_to_build_machine.sh" neste projeto, para permitir a sincronização fácil de arquivos em desenvolvimento de um PC (por exemplo: um computador que eu codifique) para outro (por exemplo: computador mais poderoso em que construí): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .
while IFS= read -r line
do
echo " git add \"$line\""
git add "$line"
done < "$FILES_STAGED"
git add
-lo: É possível `git add` uma lista de arquivos de um arquivo?