arrays - array - matriz en bash
Bash citó la expansión de matriz (6)
Otro enfoque
# echo_array.sh
contains_space(){ [[ "$1" =~ " " ]]; return $?; }
maybe_quote_one(){ contains_space "$1" && echo /'"$1"/' || echo "$1"; }
maybe_quote(){ while test "$1"; do maybe_quote_one "$1"; shift; done; }
arridx(){ echo ''${''$1''[''$2'']}''; }
arrindir(){ echo $(eval echo `arridx $1 $2`); }
arrsize(){ echo `eval echo ''${''#$1''[@]}''`; }
echo_array()
{
echo -n "$1=( "
local i=0
for (( i=0; i < `arrsize a`; i++ )); do
echo -n $(maybe_quote "$(arrindir $1 $i)") ''''
done
echo ")"
}
Uso:
source echo_array.sh
a=( foo bar baz "It was hard" curious ''67 - 00'' 44 ''my index is 7th'' )
echo_array a
# a=( foo bar baz ''It was hard'' curious ''67 - 00'' 44 ''my index is 7th'' )
arrindir a 7
# my index is 7th
arrindir a 0
# foo
arrsize a
# 8
$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
Cuando escribo un programa de bash normalmente construyo llamadas como las siguientes:
declare -a mycmd=( command.ext "arg1 with space" arg2 thing etc )
"${mycmd[@]}" || echo "Failed: foo"
Donde die foo
es una función de bash que imprime Error foo
y sale.
Pero si quiero aclarar el motivo del error, quiero imprimir el comando fallido:
"${mycmd[@]}" || echo "Failed: foo: ${mycmd[*]}"
Así que el usuario puede ejecutar el comando muerto y descubrir por qué . Sin embargo, la cotización se pierde en esta pasada: los argumentos de los mensajes con error que tienen espacios o caracteres escapados no se imprimen de una manera que se pueda cortar y pegar y ejecutar.
¿Alguien tiene una sugerencia para una forma compacta de solucionar este problema?
Creo que el problema es la forma en que bash trata el análisis de argumentos para los comandos, y la forma en que (integrado) maneja los argumentos. Otra forma de plantear el problema es:
¿Cómo puedo imprimir las comillas alrededor de los argumentos con espacios en el siguiente ejemplo de bash (que debe ejecutarse como un script, no en modo inmediato):
#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
resultado actual:
1 2 3 4
1 2 3 4
resultado deseado
1 2 3 4
1 2 "3 4"
O
1 2 3 4
"1" "2" "3 4"
En pocos caracteres de código bash adicionales.
Pregunta cerrada: @camh contestó magníficamente:
script actualizado:
#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
echo $(printf "''%s'' " "${myargs[@]}")
salida:
1 2 3 4
1 2 3 4
''1'' ''2'' ''3 4''
¿Qué hay de declare -p quotedarray
?
editar
De hecho, declare -p quotedarray
satisfará bien su propósito. Si insiste en el formato de salida del resultado, entonces tengo un pequeño truco que haría el trabajo, pero solo para la matriz indexada no asociativa.
declare -a quotedarray=(1 2 "3 4")
temparray=( "${quotedarray[@]/#//"}" ) #the outside double quotes are critical
echo ${temparray[@]/%//"}
El comando printh de bash tiene un formato% q que agrega comillas apropiadas a las cadenas a medida que se imprimen:
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
Eso sí, su idea de la "mejor" forma de citar algo no siempre es la mía, por ejemplo, tiende a preferir escapar de personajes divertidos en lugar de envolver la cadena entre comillas. Por ejemplo:
crlf=$''/r/n''
declare -a mycmd=( command.ext "arg1 with space ''n apostrophe" "arg2 with control${crlf} characters" )
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
Huellas dactilares:
Failed: foo: command.ext arg1/ with/ space/ /'n/ apostrophe $''arg2 with control/r/n characters''
Me gusta poner el código en una función para que sea más fácil de reutilizar y documentar:
function myjoin
{
local list=("${@}")
echo $(printf "''%s'', " "${list[@]}")
}
declare -a colorlist=()
colorlist+=(''blue'')
colorlist+=(''red'')
colorlist+=(''orange'')
echo "[$(myjoin ${colorlist[@]})]"
Observe cómo agregué la coma en la solución porque estoy generando código con un script bash.
[EDIT: corregir el problema que EM0 señaló que está regresando [''azul'',]]
Tu problema es con el echo
. Está obteniendo el número correcto de parámetros, con algunos parámetros que contienen espacios, pero su salida pierde la distinción de espacios entre parámetros y espacios dentro de parámetros.
En su lugar, puede usar printf(1)
para generar los parámetros e incluir siempre comillas, haciendo uso de la función printf que aplica la cadena de formato sucesivamente a los parámetros cuando hay más parámetros que especificadores de formato en la cadena de formato:
echo "Failed: foo:" $(printf "''%s'' " "${mycmd[@]}")
Eso pondrá comillas simples alrededor de cada argumento, incluso si no es necesario:
Failed: foo: ''command.ext'' ''arg1 with space'' ''arg2'' ''thing'' ''etc''
He usado comillas simples para garantizar que otros metacaracteres de shell no sean mal manejados. Esto funcionará para todos los caracteres excepto la comilla simple, es decir, si tiene un parámetro que contiene una comilla simple, la salida del comando anterior no se cortará y pegará correctamente. Es probable que esto sea lo más cerca que pueda estar sin ensuciarse.
Edit: Casi 5 años después y desde que respondí esta pregunta, se ha lanzado la versión 4.4 de bash. Esto tiene la expansión "${var@Q}"
que cita la variable de tal manera que puede ser analizada por bash.
Esto simplifica esta respuesta a:
echo "Failed: foo: " "${mycmd[@]@Q}"
Esto manejará correctamente las comillas simples en un argumento, que mi versión anterior no lo hizo.
Un método engorroso (que solo cita los argumentos que contienen espacios):
declare -a myargs=(1 2 "3 4")
for arg in "${myargs[@]}"; do
# testing if the argument contains space(s)
if [[ $arg =~ / ]]; then
# enclose in double quotes if it does
arg=/"$arg/"
fi
echo -n "$arg "
done
Salida:
1 2 "3 4"
Por cierto, con respecto a la quoting is lost on this pass
, tenga en cuenta que las cotizaciones nunca se guardan. " "
es un carácter especial que le dice al shell que trate todo lo que está dentro como un solo campo / argumento (es decir, no lo divida). Por otro lado, se conservan las comillas literales (escritas así /"
).