vectores matriz array arrays bash quotes

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í /" ).