2010-11-19 6 views
90

¿Cómo convierto el contenido del archivo en un argumento para un comando de Unix?argumentos de la línea de comando de un contenido de archivo

Volviendo un argumento en el contenido del archivo se hace como:

echo ABC > file.txt 

Pero la otra dirección?

+2

"arguments", plural, or "argument", single? La respuesta aceptada es correcta solo en el caso de un solo argumento, que es lo que consulta el texto del cuerpo, pero no en el caso de múltiples argumentos, sobre el cual parece que el tema investiga. –

+0

Las 4 respuestas siguientes generalmente tienen resultados idénticos, pero tienen una semántica ligeramente diferente. Ahora recuerdo por qué dejé de escribir scripts bash: P – Navin

Respuesta

135

Si su cáscara es bash (entre otros), un acceso directo para $(cat afile) es $(< afile), por lo que iba a escribir:

mycommand "$(< file.txt)" 

documentado en el la página de manual de bash en la sección 'Sustitución de comandos'.

Alterately, haga que su orden de lectura de la entrada estándar, por lo que: mycommand < file.txt

+4

Para ser pedante, no es un atajo para usar el operador '<'. Significa que el propio shell realizará la redirección en lugar de ejecutar el binario 'cat' para sus propiedades de redirección. – lstyls

+0

¿Hay un límite en la cantidad de argumentos? Si es así, ¿cómo puedo superarlo? – becko

+0

Es una configuración de kernel por lo que necesitaría recompilar el kernel. O investigue el comando xargs –

13

se hace eso utilizando acentos abiertos:

echo World > file.txt 
echo Hello `cat file.txt` 
+0

Esto no crea un argumento * an * - porque no está entre comillas, está sujeto a la división de cadenas, por lo que si emites '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. –

+0

... y porque está ejecutando expansión global, no emite * exactamente * lo que hay en el archivo. ** Y ** porque está realizando una operación de sustitución de comando, es extremadamente ineficiente, en realidad es 'fork()' que arranca una subcadena con un FIFO adjunto a su stdout, y luego invocando '/ bin/cat' como hijo de esa subshell, luego leyendo el resultado a través de FIFO; compare con '$ (

9
command `< file` 

pasará el contenido del archivo al comando de la entrada estándar, pero se tira de los saltos de línea, lo que significa que no se puede iterar sobre cada línea individual. Para que se podría escribir un guión con un 'para' bucle:

for i in `cat input_file`; do some_command $i; done 
+0

http://mywiki.wooledge.org/DontReadLinesWithFor –

+0

... para obtener el enfoque correcto, http://mywiki.wooledge.org/BashFAQ/001 –

+0

Además, poner '

30

Como ya se ha mencionado, puede utilizar los acentos abiertos 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 espacios en blanco, dando cada argumento a su comando como argumento . Y aunque 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 el archivo contiene:

a "b c" d 

los argumentos que obtendrá son:

a 
"b 
c" 
d 

Si desea tirar de cada línea como un argumento, utilice el tiempo/leer/no construir:

while read i ; do command_name $i ; done < filename 
+0

Debería haber mencionado, estoy asumiendo que estás usando bash. Me doy cuenta de que hay otros proyectiles por ahí, pero casi todas las máquinas * nix en las que he trabajado ejecutaron bash o algún equivalente.IIRC, esta sintaxis debería funcionar igual en ksh y zsh. – Will

+1

La lectura debe ser 'read -r' a menos que desee expandir secuencias de escape-barras invertidas, y NUL es un delimitador más seguro que la nueva línea, particularmente si los argumentos que está pasando son cosas como nombres de archivo, que pueden contener líneas nuevas literales . Además, sin borrar IFS, obtiene espacios en blanco iniciales y finales borrados implícitamente de 'i'. –

3

En mi Bash los siguientes trabajó como un encanto:

cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;' 

donde archivo_entrada es

arg1 
arg2 
arg3 

Como es evidente, esto le permite ejecutar varios comandos con cada línea de archivo_entrada, un pequeño truco que aprendió here.

+1

Su "pequeño truco" es peligroso desde una perspectiva de seguridad; si tiene un argumento que contenga '$ (somecommand)', obtendrá ese comando ejecutado en lugar de pasarlo como texto. Del mismo modo, '>/etc/passwd' se procesará como una redirección y sobrescribirá'/etc/passwd' (si se ejecuta con los permisos apropiados), etc. –

+1

En su lugar, es mucho más seguro hacer lo siguiente (en un sistema con extensiones GNU) : 'xargs -d $ '\ n' sh -c 'para arg; do command1 "$ arg"; command2 "arg"; command3 "arg"; hecho '_' - y también más eficiente, ya que pasa tantos argumentos a cada capa como sea posible en lugar de comenzar un shell por línea en su archivo de entrada. –

5

Si desea hacer esto de forma robusta, funciona para todos los argumentos de línea de comando posibles (valores con espacios, valores con líneas nuevas, valores con caracteres de comillas, valores no imprimibles, valores con caracteres globales, etc.) se pone un poco más interesante.


escribir en un archivo, teniendo en cuenta una serie de argumentos:

printf '%s\0' "${arguments[@]}" >file 

... reemplazar con "argument one", "argument two", etc., según corresponda.


para leer desde ese archivo y utilizar su contenido (en Bash, ksh93, u otra shell reciente con arrays):

declare -a args=() 
while IFS='' read -r -d '' item; do 
    args+=("$item") 
done <file 
run_your_command "${args[@]}" 

para leer desde ese archivo y utilizar su contenido (en un shell sin matrices, tenga en cuenta que sobrescribirá la lista de argumentos de la línea de comandos local y, por lo tanto, se realiza mejor dentro de una función, de modo que sobrescribe los argumentos de la función y no la lista global):

set -- 
while IFS='' read -r -d '' item; do 
    set -- "[email protected]" "$item" 
done <file 
run_your_command "[email protected]" 

Tenga en cuenta que -d (que permite utilizar un delimitador de final de línea diferente) es una extensión que no es POSIX, y un shell sin matrices tampoco puede admitirlo. Si ese fuera el caso, puede que tenga que utilizar un lenguaje no-shell para transformar el contenido delimitado en NULL en una forma eval -safe:

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 "[email protected]" 
4

Así es como me pase contenido de un archivo como argumento a una comando:

./foo --bar "$(cat ./bar.txt)" 
+1

Esto es, pero por ser sustancialmente menos eficiente, efectivamente equivalente a '$ (

Cuestiones relacionadas