shell: Cadeia de cotação com aspas simples em vez de barras invertidas

4

Como posso citar uma string com aspas simples?

Por exemplo, eu posso fazer:

$ printf "%q\n" 'two words'
two\ words
$

Existe uma maneira de obter uma string entre aspas simples (ou dupla) como saída, ou seja:

$ MAGIC 'two words'
'two words'
$

Acho a versão com aspas simples muito mais fácil de ler.

Eu gostaria de uma resposta que funcione para {ba, z} sh. O shell POSIX seria um bônus.

    
por Tom Hale 23.05.2018 / 06:14

4 respostas

4

Supondo que:

$ value=$'This isn\'t a \n\x1b "correct" test'
$ printf '%s\n' "$value"
This isn't a
"correct" test

quote () { printf %s\n "$1" | sed "s/'/'\\''/g;1s/^/'/;\$s/\$/'/" ; }

Uso:

$ quote "${value}"
'This isn'\''t a
"correct" test'

De truques sh posix do Rich

This function simply replaces every instance of «'» (single quote) within the string with «'\''» (single quote, backslash, single quote, single quote), then puts single quotes at the beginning and end of the string. Since the only character whose meaning is special within single quotes is the single quote character itself, this is totally safe. Trailing newlines are handled correctly, and the single quote at the end doubles as a safety character to prevent command substitution from clobbering the trailing newlines, should one want to do something like:

 quoted=$(quote "$var")

Atenção: os caracteres ESC (\ 033 ou \ x1b ou decimal 27) acima são citados (tecnicamente), mas são invisíveis. Quando enviado para um terminal, como outros personagens de controle, pode até causar danos. Somente quando eles são apresentados visualmente como $ '\ 033', $ '\ C- [' ou $ '\ E', eles são claramente visíveis e não ambíguos.

printf '%s\n' "${value@Q}" $'This isn\'t a \n\E "correct" test'

printf '%s\n' ${(q)value} This\ isn\'t\ a\ $'\n'$'3'\ \"correct\"\ test
printf '%s\n' ${(qq)value} 'This isn'\''t a "correct" test'
printf '%s\n' ${(qqq)value} "This isn't a \"correct\" test"
printf '%s\n' ${(qqqq)value} $'This isn\'t a \n3 "correct" test' printf '%s\n' ${(q-)value} 'This isn'\''t a "correct" test' printf '%s\n' ${(q+)value} $'This isn\'t a \n\C-[ "correct" test'

Tenha cuidado com algumas strings entre aspas: os caracteres ESC (\ 033 ou \ x1b ou decimal 27) acima são todos (tecnicamente) citados, mas invisíveis. Quando enviado para um terminal, como outros personagens de controle, pode até causar danos. Somente quando eles são apresentados visualmente como $ '\ 033', $ '\ C- [' ou $ '\ E', eles são claramente visíveis e não ambíguos.

Em manual do Bash :

${parameter@operator}
Q The expansion is a string that is the value of parameter quoted in a format that can be reused as input.

Na zshexpn man page :

q
Quote characters that are special to the shell in the resulting words with backslashes; unprintable or invalid characters are quoted using the $'\NNN' form, with separate quotes for each octet.

If this flag is given twice, the resulting words are quoted in single quotes and if it is given three times, the words are quoted in double quotes; in these forms no special handling of unprintable or invalid characters is attempted. If the flag is given four times, the words are quoted in single quotes preceded by a $. Note that in all three of these forms quoting is done unconditionally, even if this does not change the way the resulting string would be interpreted by the shell.

If a q- is given (only a single q may appear), a minimal form of single quoting is used that only quotes the string if needed to protect special characters. Typically this form gives the most readable output.

If a q+ is given, an extended form of minimal quoting is used that causes unprintable characters to be rendered using $'...'. This quoting is similar to that used by the output of values by the typeset family of commands.

    
por 23.05.2018 / 07:11
3

O Zsh tem várias opções de cotação que podem ser aplicadas à expansão de parâmetros :

q

Quote characters that are special to the shell in the resulting words with backslashes; unprintable or invalid characters are quoted using the $'\NNN' form, with separate quotes for each octet.

If this flag is given twice, the resulting words are quoted in single quotes and if it is given three times, the words are quoted in double quotes; in these forms no special handling of unprintable or invalid characters is attempted. If the flag is given four times, the words are quoted in single quotes preceded by a $. Note that in all three of these forms quoting is done unconditionally, even if this does not change the way the resulting string would be interpreted by the shell.

If a q- is given (only a single q may appear), a minimal form of single quoting is used that only quotes the string if needed to protect special characters. Typically this form gives the most readable output.

If a q+ is given, an extended form of minmal quoting is used that causes unprintable characters to be rendered using $'...'. This quoting is similar to that used by the output of values by the typeset family of commands.

Então, uma função como:

MAGIC () {
    printf "%s\n" "${(q+)@}"
}

daria saída como:

$ MAGIC 'two words'
'two words'
$ MAGIC 'two words "'
'two words "'
$ MAGIC 'two '"'"'words'
'two '\''words'
    
por 23.05.2018 / 06:41
2

Aqui está uma solução bastante simples usando sed. A entrada está em $raw , a saída está em $quoted .

quoted=$(printf '%sz\n' "$raw" | sed "s/'/'\\''/g; s/'''/'/g")
quoted="'${quoted%z}'"

O truque com o z é lidar corretamente com as novas linhas. Com apenas printf %s "$raw" , você precisa confiar no comportamento do sed quando sua entrada não termina com uma nova linha e, em seguida, a substituição de comando sempre come todas as novas linhas finais.

A segunda substituição no script sed não é necessária, mas produz uma saída ligeiramente mais agradável, evitando '' inútil na saída quando há ' 'consecutivos na entrada.

Aqui está uma solução POSIX sh pura (também funciona em zsh, mesmo no modo nativo). Ele também evita '' , mas mantém '' para a string vazia.

tail=$raw
quoted=
sq=\'
while
  case "$tail" in
    '') false;;
    \'*) quoted="$quoted\'"; tail="${tail#?}";;
    [!\']*\'*) quoted="$quoted'${tail%%$sq*}'\'"; tail="${tail#*$sq}";;
    *) quoted="$quoted'${tail%%$sq*}'"; false;;
  esac
do
  :
done
if [ -z "$quoted" ]; then quoted="''"; fi
    
por 23.05.2018 / 23:13
-2

Você pode citar a string uma segunda vez com aspas duplas.

$ printf "%s\n" "'two words'"
\'two\ words\'
$ _

Eu não acho que o printf do shell POSIX saiba sobre %q .

Edit: Se você está falando sobre o MAGIC , então você poderia fazer:

$ printf "%s\n" "'two words'"
'two words'
$ _
    
por 23.05.2018 / 06:18