Digamos que tengo un archivo que foo.txt
especifica N
argumentos
arg1
arg2
...
argN
que necesito pasar al comando my_command
¿Cómo uso las líneas de un archivo como argumentos de un comando?
Digamos que tengo un archivo que foo.txt
especifica N
argumentos
arg1
arg2
...
argN
que necesito pasar al comando my_command
¿Cómo uso las líneas de un archivo como argumentos de un comando?
Respuestas:
Si su shell es bash (entre otros), un atajo para $(cat afile)
es $(< afile)
, por lo que escribiría:
mycommand "$(< file.txt)"
Documentado en la página de manual de bash en la sección 'Sustitución de comandos'.
Alternativamente, haga que su comando se lea desde stdin, entonces: mycommand < file.txt
<
operador. Significa que el propio shell realizará la redirección en lugar de ejecutar elcat
binario por sus propiedades de redirección.
"$(…)"
, el contenido de file.txt
se pasaría a la entrada estándar de mycommand
, no como un argumento. "$(…)"
significa ejecutar el comando y devolver la salida como una cadena ; aquí el "comando" solo lee el archivo pero podría ser más complejo.
Como ya se mencionó, puede usar los backticks o $(cat filename)
.
Lo que no se mencionó, y creo que es importante tener en cuenta, es que debe recordar que el shell dividirá el contenido de ese archivo según el espacio en blanco, dando cada "palabra" que encuentre a su comando como argumento. Y si bien puede encerrar un argumento de línea de comandos entre comillas para que pueda contener espacios en blanco, secuencias de escape, etc., leer del archivo no hará lo mismo. Por ejemplo, si su archivo contiene:
a "b c" d
los argumentos que obtendrás son:
a
"b
c"
d
Si desea extraer cada línea como argumento, use la construcción while / read / do:
while read i ; do command_name $i ; done < filename
read -r
menos que desee expandir las secuencias de escape de barra diagonal inversa, y NUL es un delimitador más seguro que la nueva línea, particularmente si los argumentos que está pasando son nombres de archivos, que pueden contener nuevas líneas literales. Además, sin borrar IFS, obtiene espacios en blanco iniciales y finales borrados implícitamente i
.
command `< file`
pasará el contenido del archivo al comando en stdin, pero eliminará las nuevas líneas, lo que significa que no puede iterar sobre cada línea individualmente. Para eso, podría escribir un script con un bucle 'for':
for line in `cat input_file`; do some_command "$line"; done
O (la variante multilínea):
for line in `cat input_file`
do
some_command "$line"
done
O (variante multilínea con en $()
lugar de ``
):
for line in $(cat input_file)
do
some_command "$line"
done
< file
backticks significa que en realidad no realiza una redirección command
en absoluto.
command `< file`
No funciona ni en bash ni en POSIX sh; zsh es un asunto diferente, pero ese no es el tema del que trata esta pregunta).
Hello World
en una línea. Lo verás primero some_command Hello
, luego some_command World
, no some_command "Hello World"
o some_command Hello World
.
Lo haces usando backticks:
echo World > file.txt
echo Hello `cat file.txt`
echo "Hello * Starry * World" > file.txt
en el primer paso, obtendría al menos cuatro argumentos separados pasados al segundo comando, y probablemente más, ya que el *
s se expandiría a los nombres de los archivos presentes en el directorio actual.
fork()
saliendo de una subshell con un FIFO adjunto a su stdout, luego invocando /bin/cat
como hijo de esa subshell, luego leyendo la salida a través del FIFO; compare con $(<file.txt)
, que lee el contenido del archivo en bash directamente sin subcapas ni FIFO involucrados.
Si desea hacer esto de una manera robusta que funcione para todos los argumentos posibles de la línea de comando (valores con espacios, valores con líneas nuevas, valores con comillas literales, valores no imprimibles, valores con caracteres globales, etc.), se pone un poco más interesante.
Para escribir en un archivo, dada una serie de argumentos:
printf '%s\0' "${arguments[@]}" >file
... reemplazar con "argument one"
, "argument two"
, etc., según corresponda.
Para leer ese archivo y usar su contenido (en bash, ksh93 u otro shell reciente con matrices):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
Para leer ese archivo y usar su contenido (en un shell sin matrices; tenga en cuenta que esto sobrescribirá su lista de argumentos de la línea de comandos local, y por lo tanto, es mejor hacerlo dentro de una función, de modo que sobrescriba los argumentos de la función y no la lista global):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Tenga en cuenta que -d
(permitir que se use un delimitador de fin de línea diferente) es una extensión que no es POSIX, y un shell sin matrices también puede no ser compatible. Si ese fuera el caso, es posible que deba usar un lenguaje que no sea shell para transformar el contenido delimitado por NUL en una eval
forma segura:
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 "$@"
Así es como paso el contenido de un archivo como argumento a un comando:
./foo --bar "$(cat ./bar.txt)"
$(<bar.txt)
(cuando se usa un shell, como bash, con la extensión adecuada). $(<foo)
es un caso especial: a diferencia de la sustitución regular de comandos, como se usa en $(cat ...)
, no se bifurca en un subshell para operar, y por lo tanto evita una gran cantidad de sobrecarga.
Si todo lo que necesita hacer es convertir el archivo arguments.txt
con contenido
arg1
arg2
argN
en my_command arg1 arg2 argN
entonces puede simplemente utilizar xargs
:
xargs -a arguments.txt my_command
Puede poner argumentos estáticos adicionales en la xargs
llamada, como xargs -a arguments.txt my_command staticArg
cuál llamarámy_command staticArg arg1 arg2 argN
En mi bash shell, lo siguiente funcionó a las mil maravillas:
cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'
donde input_file es
arg1
arg2
arg3
Como es evidente, esto le permite ejecutar múltiples comandos con cada línea desde input_file, un pequeño truco que aprendí aquí .
$(somecommand)
, ejecutará ese comando en lugar de pasarlo como texto. Del mismo modo, >/etc/passwd
se procesará como una redirección y sobrescritura /etc/passwd
(si se ejecuta con los permisos adecuados), etc.
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
y también más eficiente, ya que pasa tantos argumentos a cada shell como sea posible en lugar de comenzar un shell por línea en su archivo de entrada.
cat
Después de editar la respuesta de @Wesley Rice un par de veces, decidí que mis cambios eran demasiado grandes para continuar cambiando su respuesta en lugar de escribir la mía. Entonces, ¡decidí que necesitaba escribir el mío!
Lea cada línea de un archivo y opere en él línea por línea de esta manera:
#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
Esto viene directamente del autor Vivek Gite aquí: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . ¡Obtiene el crédito!
Sintaxis: Lea el archivo línea por línea en un shell Bash Unix y Linux:
1. La sintaxis es la siguiente para bash, ksh, zsh y todos los demás shells para leer un archivo línea por línea
2.while read -r line; do COMMAND; done < input.file
3. La-r
opción pasada al comando de lectura evita que se interpreten los escapes de barra invertida.
4. Agregue laIFS=
opción antes del comando de lectura para evitar que se recorte el espacio en blanco inicial / final -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file
Y ahora para responder a esta pregunta ahora cerrada que también tuve: ¿Es posible 'agregar' una lista de archivos de un archivo?- Aquí está mi respuesta:
Tenga en cuenta que FILES_STAGED
es una variable que contiene la ruta absoluta a un archivo que contiene un montón de líneas donde cada línea es una ruta relativa a un archivo que me gustaría hacer git add
. Este fragmento de código está a punto de formar parte del archivo "eRCaGuy_dotfiles / útiles_scripts / sync_git_repo_to_build_machine.sh" en este proyecto, para permitir la sincronización fácil de archivos en desarrollo desde una PC (por ejemplo: una computadora que codifico) a otra (por ejemplo: un computadora más potente sobre la que construyo): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .
while IFS= read -r line
do
echo " git add \"$line\""
git add "$line"
done < "$FILES_STAGED"
git add
en él: ¿Es posible `git add` una lista de archivos de un archivo?