2011-06-10 18 views
26

Tengo una lista de cadenas y quiero pasar esas cadenas como argumentos en una sola llamada de línea de comando Bash. Para cadenas alfanuméricas simples basta con simplemente pasarlos textualmente:¿Cómo puedo escapar de una cadena arbitraria para utilizarla como un argumento de línea de comando en Bash?

> script.pl foo bar baz yes no 
foo 
bar 
baz 
yes 
no 

entiendo que si un argumento contiene espacios o barras invertidas o comillas dobles, necesito barra invertida-escapar de las comillas dobles y las barras invertidas, y luego doblar -quitar el argumento.

> script.pl foo bar baz "\"yes\"\\\"no\"" 
foo 
bar 
baz 
"yes"\"no" 

Pero cuando un argumento contiene un signo de exclamación, esto sucede:

> script.pl !foo 
-bash: !foo: event not found 

citando doble no funciona:

> script.pl "!foo" 
-bash: !foo: event not found 

Tampoco se da cuenta de barra invertida-escape (cómo el literal barra invertida está presente en la salida):

> script.pl "\!foo" 
\!foo 

Aún no sé mucho sobre Bash, pero sé que hay otros personajes especiales que hacen cosas similares. ¿Cuál es el procedimiento general para escaparse con seguridad de una cadena arbitraria para utilizarla como un argumento de línea de comando en Bash? Supongamos que la cadena puede tener una longitud arbitraria y contener combinaciones arbitrarias de caracteres especiales. Me gustaría una subrutina escape() que puedo usar de la siguiente manera (Perl ejemplo):

$cmd = join " ", map { escape($_); } @args; 

Estos son algunos más cadenas ejemplo que debe ser escaparon con seguridad por esta función (Sé que algunos de éstos se ven similar a Windows, eso es deliberada):

yes 
no 
Hello, world  [string with a comma and space in it] 
C:\Program Files\ [path with backslashes and a space in it] 
"     [i.e. a double-quote] 
\     [backslash] 
\\    [two backslashes] 
\\\    [three backslashes] 
\\\\    [four backslashes] 
\\\\\    [five backslashes] 
"\    [double-quote, backslash] 
"\T    [double-quote, backslash, T] 
"\\T    [double-quote, backslash, backslash, T] 
!1     
!A     
"!\/'"   [double-quote, exclamation, backslash, forward slash, apostrophe, double quote] 
"Jeff's!"   [double-quote, J, e, f, f, apostrophe, s, exclamation, double quote] 
$PATH    
%PATH%    
&     
<>|&^    
*@[email protected]#?-_  

EDIT:

Sería esto el truco? Escape a cada carácter inusual con una barra diagonal inversa, y omita comillas simples o dobles. (El ejemplo está en Perl, pero cualquier idioma puede hacerlo)

sub escape { 
    $_[0] =~ s/([^a-zA-Z0-9_])/\\$1/g; 
    return $_[0]; 
} 
+0

Una respuesta bastante buena está aquí: http: //unix.stackexchange.com/q/4770/5779 –

+0

El script de Perl proporcionado (en la edición) es incorrecto. Falla para líneas nuevas, que cuando escapan con una barra invertida son simplemente ignoradas. –

Respuesta

0

Bash interpreta los signos de exclamación solo en el modo interactivo.

Puede evitar esto haciendo:

set +o histexpand 

entre comillas dobles que debe escapar de signos de dólar, comillas dobles, barras invertidas y yo diría que eso es todo.

+0

Esto es útil, pero sería bueno si pudiera evitar tener que apagar y encender histexpand para cada llamada de comando. – qntm

+0

si está escribiendo un script de shell no es necesario. los signos de admiración solo se interpretan en modo interactivo. Puedes apagarlo en tu bashrc. – Benoit

+0

No estoy escribiendo un script de shell. – qntm

1

Puede usar comillas simples para escaparse de cadenas para Bash. Sin embargo, tenga en cuenta que esto no expande las variables entre comillas como lo hacen las comillas dobles. En su ejemplo, lo siguiente debería funcionar:

script.pl '!foo' 

De Perl, esto depende de la función que está utilizando para generar el proceso externo. Por ejemplo, si usa la función system, puede pasar argumentos como parámetros para que no haya necesidad de escapar de ellos.Por supuesto que "fija D necesita para escapar de cotizaciones para Perl:

system("/usr/bin/rm", "-fr", "/tmp/CGI_test", "/var/tmp/CGI"); 
+2

El uso de comillas simples no parece funcionar si el argumento contiene una comilla única, p. Ej. '' ¡Jeff's! '. Una comilla simple no puede ser una barra invertida escapada tampoco. – qntm

+0

@qntm: Use lo siguiente: '" Jeff's "'!''. Piense en ello como dos tokens separados y se encuentra uno al lado del otro: '" Jeff's '' y ''!' ' –

+1

Para usar una comilla simple en una sola cadena citada, reemplácela con' \ ''. Por ejemplo, 'Jeff' \ '' s! ' . Esto es proclive para un humano, pero es agradable y simple para un guión: primero use una expresión regular para convertir 'a' \ '' y luego encierre en '...'. –

17

Si desea citar de forma segura nada de Bash, puede utilizar su base de printf %q formato:

cat strings.txt:

yes 
no 
Hello, world 
C:\Program Files\ 
" 
\ 
\\ 
\\\ 
\\\\ 
\\\\\ 
"\ 
"\T 
"\\T 
!1 
!A 
"!\/'" 
"Jeff's!" 
$PATH 
%PATH% 
& 
<>|&^ 
*@[email protected]#?-_ 

cat quote.sh:

#!/bin/bash 
while IFS= read -r -d $'\n' 
do 
    printf %q "$REPLY" 
    printf '\n' 
done < strings.txt 

./quote.sh:

yes 
no 
Hello\,\ world 
C:\\Program\ Files\\ 
\" 
\\ 
\\\\ 
\\\\\\ 
\\\\\\\\ 
\\\\\\\\\\ 
\"\\ 
\"\\T 
\"\\\\T 
\!1 
\!A 
\"\!\\/\'\" 
\"Jeff\'s\!\" 
\$PATH 
%PATH% 
\& 
\<\>\|\&\^ 
\*@\$\$A\[email protected]#\?-_ 

Estas cadenas pueden ser copiados textualmente a echo por ejemplo a la salida de las cadenas originales en strings.txt.

+0

Esperaba que cada uno de '\"! '$ <> | &^'Necesitara escapar, pero noté que printf también ha escapado de' * 'y'? 'En la cadena final. ¿Me he perdido alguna? hay una lista completa de caracteres que deben ser de barra invertida-escapada? – qntm

+1

Bueno, parece que cualquier personaje puede ser de barra invertida-escapada sin penalización, por lo que el curso de acción más seguro es escapar de todo lo que no sean letras, números y el guión bajo. , la función que estoy describiendo es: 'sub escape {$ _ [0] = ~ s/([^ a-zA-Z0-9 _])/\\ $ 1/g; return $ _ [0];}' – qntm

+0

@qntm: ¿Pero qué sucede si la cuerda (o parte de ella) ya se escapó? Tendría doble escape, uno de los cinco jinetes de la codepocalypse. – l0b0

0

Esta no es una respuesta completa, pero a veces me resulta útil combinar dos tipos de comillas para una sola cadena concatenandolas, por ejemplo echo "$HOME"'/foo!?.*'.

1
sub text_to_shell_lit(_) { 
    return $_[0] if $_[0] =~ /^[a-zA-Z0-9_\-]+\z/; 
    my $s = $_[0]; 
    $s =~ s/'/'\\''/g; 
    return "'$s'"; 
} 

Ver este anterior post para un ejemplo.

1

Siempre que vea no obtiene el resultado deseado, utilice el siguiente método:

"""\special character"""

donde carácter especial puede incluir ! " *^% $ # @ ....

Por ejemplo, si Si desea crear un bash que genere otro archivo bash en el que hay una cadena y desea asignarle un valor, puede tener el siguiente escenario de ejemplo:

Area="(1250,600),(1400,750)" 
printf "SubArea="""\""""${Area}"""\""""\n" > test.sh 
printf "echo """\$"""{SubArea}" >> test.sh 

Entonces test.sh archivo tendrá el siguiente código:

SubArea="(1250,600),(1400,750)" 
echo ${SubArea} 

Como recordatorio para tener nueva línea \n, debemos utilizar printf.

6

¿Cuál es el procedimiento general para escaparse con seguridad de una cadena arbitraria para utilizarla como un argumento de línea de comando en Bash?

reemplazar cada ocurrencia de ' con '\'', a continuación, poner ' al principio y al final.

Todos los caracteres a excepción de una comilla simple se pueden usar textualmente en una cadena delimitada por una sola comilla. No hay forma de poner una comilla simple dentro de una cadena delimitada por comillas simples, pero eso es bastante fácil de solucionar: termine la cadena ('), luego agregue una comilla simple usando una barra diagonal inversa para escapar (\'), luego comience una nueva cadena (').

Hasta donde yo sé, esto siempre funcionará, sin excepciones.

+0

Aparte de la respuesta 'printf' (que solo funciona si ya está en bash), esta es la única respuesta correcta en el hilo. –

Cuestiones relacionadas