(OK, desculpe, eu li sua pergunta muito rapidamente, então algumas das minhas respostas estão um pouco além do ponto, ainda deixando como é útil para você ou alguns)
Há várias coisas a considerar aqui.
citação das variáveis shell
Deixar uma variável sem aspas em shells POSIX (em contextos de lista, como em argumentos para um comando), não awk
, é o operador split + glob.
Se você fizer isso:
cmd foo=$var
Em que $var
é * *
.
Não solicite ao shell que divida o conteúdo de $var
com base no valor da variável de shell $IFS
especial, por padrão em espaços em branco. Portanto, acima disso, isso nos dá foo=*
e *
e executa globbing em cada um desses, que expande foo=*
para todos os nomes de arquivos no diretório atual que começam com foo=
e *
para todos os nomes não nomes de arquivos ocultos.
Então, na verdade, você deve quase sempre citar suas variáveis shell , sejam elas argumentos para awk
ou não. Isso também se aplica à substituição do comando do shell ( '...'
e $(...)
) e à expansão aritmética do shell ( $((...))
).
passando dados como estão para awk
O outro problema é que awk
(não o shell) expande sequências de escape de barras invertidas nas atribuições de variáveis como -v var=value
.
Por exemplo, -v var='\n/\n/'
define o conteúdo da variável awk
var
como <newline>/<newline>/
, não \n/\n/
. Isso também se aplica às variáveis awk
definidas como:
awk '...' var=value
Para passar dados para awk
sem passar por essa expansão, você pode usar as matrizes ENVIRON
ou ARGV
awk:
var=$value awk 'BEGIN {var=ENVIRON["var"]} ...'
(acima, é uma atribuição de variável shell (para uma variável não-array), então não pode ser split + glob, que é um dos raros casos em que você pode omitir as aspas ao redor das variáveis)
ou:
awk 'BEGIN {var=ARGV[1]; delete ARGV[1]} ...' "$value"
cotação e awk
variáveis
Esse split + glob é apenas um recurso de shell (mis-). A linguagem awk
é uma linguagem completamente diferente.
Em awk
, as variáveis são referenciadas a varname
, não $varname
, e aspas são usadas para introduzir strings. Portanto, "varname"
é a string varname
, enquanto varname
refere-se à variável.
sanitizando variáveis para evitar injeção de código
Estritamente falando, as variáveis shell não estão sanitizando, não estão citando as variáveis que estão usando o operador split + glob. Enquanto na maioria das linguagens você coloca aspas em torno de strings fixas, em shells, é o contrário: tudo é string e aspas são usadas para evitar algum comportamento especial, e especialmente as variáveis quase sempre devem ser citadas (uma decisão ruim de design). fez sentido na casca Bourne nos anos 70, mas é um obstáculo em conchas modernas, sendo zsh
a única casca que parcialmente fixa isso).
O shell ou o awk não avaliarão / interpretarão o código armazenado em sua própria variável, a menos que você diga a eles.
var='foo; rm -f var'
echo $var
# or
echo "$var"
Não fará com que o conteúdo da variável seja avaliado como código de shell (embora o primeiro seja submetido a divisão e globbing, o que pode ter consequências desastrosas (por exemplo, com var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*'
). Você precisaria de:
eval "echo $var"
# or
sh -c "echo $var"
para ser avaliado / interpretado como código shell.
awk
não possui esse recurso eval
. perl
/ python
do.
Mas cuidado com a contaminação cruzada. Você pode ter os dados da variável shell pass (em variáveis shell ) como código para executar por awk
:
awk '{print "'"$var"': " $0}'
seria perigoso caso a variável $var
shell contenha, por exemplo:
var='test"; print "foo" > /etc/passwd; print "blah'
porque o shell seria executado:
["awk", "{print \"test\"; print \"foo\" > /etc/passwd; print \"blah: \" $0}"]
Ou o contrário:
awk '{system("echo foo: " $0)}' < file
em que awk
executaria um shell como:
["sh", "-c", "echo foo: content-of-the-line"]
para cada linha de file
(e pense no que uma linha como ; rm -rf /
faria).
Não é apenas entre awk
e sh
. Você precisa ser cuidadoso sempre que dados variáveis / não controlados possam ser avaliados como código por outro interpretador. Exemplos são:
sed "s/$regexp/blah/g"
A linguagem de
sed
é limitada, mas ainda pode prejudicar, como acontece com regexp='//;w /etc/passwd; s/
'.
Ou:
find . -exec sh -c "echo {}" \;
Agora, para evitar esses problemas, existem duas abordagens gerais:
-
converta a variável de um intérprete para o outro. Isso funciona para o shell - > awk ou encontrar - > sh caso acima. Como mudança:
awk '{print "'"$var"': " $0}'
para:
awk -v awk_var="$var" '{print awk_var ": " $0}'
E:
find . -exec sh -c "echo {}" \;
para:
find . -exec sh -c 'echo "$1"' sh {} \;
mas isso não funcionará para o shell - > sed, ou awk - > casos de shell.
-
quando 1 não é possível, você precisa limpar as variáveis para remover ou escapar dos caracteres que podem ser um problema. Em,
awk '{system("echo foo: " $0)}'
você precisa converter $0
em algo que seja uma string limpa no que diz respeito ao shell. Uma opção é prefixar cada caractere com uma barra invertida, mas isso não funcionará para a nova linha (não é um problema aqui). Outro é colocar a string entre aspas simples e escapar de cada aspas simples.
awk 'function escape(s) {
gsub(/'\''/,"&\\&&",s)
return "'\''" s "'\''"
}
{system("echo foo: " escape($0))}'