Tengo un comando complejo del que me gustaría hacer un script de shell / bash. Puedo escribirlo en términos de $1
fácilmente:
foo $1 args -o $1.ext
Quiero poder pasar múltiples nombres de entrada al script. ¿Cuál es la forma correcta de hacerlo?
Y, por supuesto, quiero manejar nombres de archivos con espacios en ellos.
bash
command-line
Thelema
fuente
fuente
Respuestas:
Use
"$@"
para representar todos los argumentos:Esto iterará sobre cada argumento y lo imprimirá en una línea separada. $ @ se comporta como $ * excepto que cuando se citan los argumentos se dividen correctamente si hay espacios en ellos:
fuente
"$@"
es que se expande a nada cuando no hay parámetros posicionales, mientras que se"$*"
expande a la cadena vacía, y sí, hay una diferencia entre ningún argumento y un argumento vacío. Ver${1:+"$@"} in
/ bin / sh` .$var
pude ejecutar los argumentos.shift
tira$1
y baja todos los elementos posteriores.Vuelva a escribir una respuesta ahora eliminada por VonC .
Robert GambleLa respuesta sucinta de trata directamente con la pregunta. Este amplifica algunos problemas con nombres de archivos que contienen espacios.
Ver también: $ {1: + "$ @"} en / bin / sh
Tesis básica:
"$@"
es correcta y$*
(sin comillas) casi siempre está mal. Esto se debe a que"$@"
funciona bien cuando los argumentos contienen espacios, y funciona igual que$*
cuando no lo hacen. En algunas circunstancias, también"$*"
está bien, pero"$@"
generalmente (pero no siempre) funciona en los mismos lugares. Sin comillas,$@
y$*
son equivalentes (y casi siempre incorrectos).Así que, ¿cuál es la diferencia entre
$*
,$@
,"$*"
y"$@"
? Todos están relacionados con 'todos los argumentos del shell', pero hacen cosas diferentes. Cuando no se cita,$*
y$@
hacer lo mismo. Tratan cada 'palabra' (secuencia de espacios no en blanco) como un argumento separado. Sin embargo, las formas citadas son bastante diferentes:"$*"
trata la lista de argumentos como una sola cadena separada por espacios, mientras que"$@"
trata los argumentos casi exactamente como eran cuando se especificaban en la línea de comando."$@"
se expande a nada cuando no hay argumentos posicionales;"$*"
se expande a una cadena vacía, y sí, hay una diferencia, aunque puede ser difícil de percibir. Consulte más información a continuación, después de la introducción del comando (no estándar)al
.Tesis secundaria: si necesita procesar argumentos con espacios y luego pasarlos a otros comandos, a veces necesita herramientas no estándar para ayudarlo. (O debe usar matrices, con cuidado: se
"${array[@]}"
comporta de manera análoga"$@"
).Ejemplo:
¿Por qué eso no funciona? No funciona porque el shell procesa las cotizaciones antes de expandir las variables. Entonces, para que el shell preste atención a las citas incrustadas
$var
, debe usareval
:Esto se vuelve realmente complicado cuando tiene nombres de archivo como "
He said, "Don't do this!"
" (con comillas y comillas dobles y espacios).Los shells (todos ellos) no hacen que sea particularmente fácil manejar tales cosas, por lo que (curiosamente) muchos programas de Unix no hacen un buen trabajo al manejarlos. En Unix, un nombre de archivo (componente único) puede contener cualquier carácter excepto barra diagonal y NUL
'\0'
. Sin embargo, los shells no recomiendan espacios, líneas nuevas o pestañas en ningún lugar de los nombres de las rutas. También es la razón por la cual los nombres de archivo estándar de Unix no contienen espacios, etc.Cuando se trata de nombres de archivos que pueden contener espacios y otros caracteres problemáticos, debe ser extremadamente cuidadoso, y hace mucho tiempo descubrí que necesitaba un programa que no sea estándar en Unix. Lo llamo
escape
(la versión 1.1 tenía fecha de 1989-08-23T16: 01: 45Z).Aquí hay un ejemplo de
escape
uso: con el sistema de control SCCS. Es un guión de portada que hace tanto undelta
(pensar registro ) como unget
(pensar registro ). Varios argumentos, especialmente-y
(la razón por la que realizó el cambio) contendrían espacios en blanco y nuevas líneas. Tenga en cuenta que el script data de 1992, por lo que utiliza ticks de retroceso en lugar de$(cmd ...)
notación y no se utiliza#!/bin/sh
en la primera línea.(Probablemente no usaría escape tan a fondo en estos días
-e
, por ejemplo, no es necesario con el argumento, pero en general, este es uno de mis scripts más simples que usoescape
).El
escape
programa simplemente genera sus argumentos, al igual que loecho
hace, pero asegura que los argumentos estén protegidos para su uso coneval
(un nivel deeval
; Tengo un programa que realizó la ejecución remota de shell, y que necesitaba escapar de la salida deescape
).Tengo otro programa llamado
al
que enumera sus argumentos uno por línea (y es aún más antiguo: la versión 1.1 con fecha 27-01-27T14: 35: 49). Es más útil al depurar scripts, ya que se puede conectar a una línea de comando para ver qué argumentos se pasan realmente al comando.[ Agregado: Y ahora para mostrar la diferencia entre las diversas
"$@"
anotaciones, aquí hay un ejemplo más:Tenga en cuenta que nada conserva los espacios en blanco originales entre
*
y*/*
en la línea de comando. Además, tenga en cuenta que puede cambiar los 'argumentos de la línea de comandos' en el shell utilizando:Esto establece 4 opciones, '
-new
', '-opt
', 'and
' y 'arg with space
'.]
Hmm, esa es una respuesta bastante larga , tal vez exégesis es el mejor término. El código fuente está
escape
disponible a pedido (correo electrónico a firstname dot lastname en gmail dot com). El código fuente paraal
es increíblemente simple:Eso es todo. Es equivalente a la
test.sh
script que mostró Robert Gamble, y podría escribirse como una función de shell (pero las funciones de shell no existían en la versión local de Bourne cuando escribí por primera vezal
).También tenga en cuenta que puede escribir
al
como un simple script de shell:El condicional es necesario para que no produzca salida cuando no se pasan argumentos. El
printf
comando producirá una línea en blanco con solo el argumento de cadena de formato, pero el programa C no produce nada.fuente
Tenga en cuenta que la respuesta de Robert es correcta, y también funciona
sh
. Puede (de forma portátil) simplificarlo aún más:es equivalente a:
Es decir, no necesitas nada!
Prueba (
$
es símbolo del sistema):Leí por primera vez sobre esto en Unix Programming Environment por Kernighan y Pike.
En
bash
,help for
documenta esto:fuente
"$@"
significa el críptico , y una vez que sabe lo quefor i
significa, no es menos legible quefor i in "$@"
.for i
es mejor porque ahorra pulsaciones de teclas. Comparé la legibilidad defor i
yfor i in "$@"
.Para casos simples también puedes usar
shift
. Trata la lista de argumentos como una cola. Cada unoshift
arroja el primer argumento y se disminuye el índice de cada uno de los argumentos restantes.fuente
shift
en un bucle for, ese elemento sigue siendo parte de la matriz, mientras que con estoshift
funciona como se esperaba.echo "$1"
para preservar el espacio en el valor de la cadena de argumento. Además, (un problema menor) esto es destructivo: no puede reutilizar los argumentos si los ha cambiado todos. A menudo, eso no es un problema: de todos modos, solo procesas la lista de argumentos una vez.--file myfile.txt
, $ 1 es el parámetro, $ 2 es el valor y llama a shift dos veces cuando necesita omitir otro argumentoTambién puede acceder a ellos como elementos de una matriz, por ejemplo, si no desea iterar a través de todos ellos
fuente
./my-script.sh param1 "param2 is a quoted param"
: - usando argv = ($ @) obtendrá [param1, param2], - usando argv = ("$ @"), obtendrá [param1, param2 es un parámetro citado]fuente
[ $# != 0 ]
es mejor. En lo anterior,#$
debe ser$#
como enwhile [[ $# > 0 ]] ...
Amplificando la respuesta de baz, si necesita enumerar la lista de argumentos con un índice (como buscar una palabra específica), puede hacerlo sin copiar la lista o mutarla.
Supongamos que desea dividir una lista de argumentos en un guión doble ("-") y pasar los argumentos antes de los guiones a un comando, y los argumentos después de los guiones a otro:
Los resultados deberían verse así:
fuente
getopt Use el comando en sus scripts para formatear cualquier opción o parámetro de la línea de comando.
fuente