Use uma variável shell para executar um comando

4

Eu tenho um comando unix em uma variável, é assim:

cmd="find /path/to/webpage -type f | grep -v .svn | xargs grep $@"
'$cmd'
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

Quando tento executar o comando $cmd em um script bash, ele não funciona. No entanto, quando copio e colo o mesmo comando, ele funciona. Você pode me deixar saber o que estou fazendo errado?

Eu tentei colocar aspas no caminho, o mesmo erro ocorre

cmd="find \"/path/to/webpage\" -type f | grep -v .svn | xargs grep $@"
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

Quando eu removo o parâmetro -type f , recebo este erro:

cmd="find /path/to/webpage | grep -v .svn | xargs grep $@"
find: invalid predicate '-v'

Isso me faz pensar que o pipe não está sendo reconhecido. O que posso fazer para que isso funcione?

    
por Roy Rico 26.07.2012 / 21:28

4 respostas

7

Outros já explicaram o que fazer. Deixe-me explicar o que está acontecendo aqui: o caractere de pipe | não faz um pipeline quando a variável é expandida, mas age como um caractere literal. Portanto, find é executado com os seguintes argumentos:

{"/path/to/webpage", "-type", "f", "|", "grep", "-v", ".svn", "|", ...}

e interpreta | como caminho e reclama que deveria ter aparecido antes da expressão ( -type f ).

Outro grande erro é que você está usando '$cmd' como a única linha de comando. Se $cmd (ou seja, find ... ) tiver êxito e produzir a saída como rm -rf / , ela será executada em seu nome. Tenha sempre cuidado ao coletar dados como código!

Melhoria 1. find ... | grep -v ... é uma maneira ruim de excluir algo da saída: find irá percorrer subdiretórios inteiros chamados .svn , produzir as linhas, apenas para serem descartadas posteriormente. Por que não dizer find para fazer isso diretamente?

find path -type f | grep -v .svn                # don't do this
find path -name .svn -prune -o -type f -print   # do this instead

Melhoria 2. Ao combinar find e xargs , use sempre -print0 em find e -0 em xargs :

find path ... -print0 | xargs -0 -r grep ...    # I'd also recommend -r

ou você pode fazer isso totalmente em grep :

grep --recursive --exclude-dir=.svn pattern path
    
por 27.07.2012 / 00:50
4

Se você realmente deseja executar código em uma variável, pode fazê-lo usando eval .

cmd="find /path/to/webpage -type f | grep -v .svn | xargs grep something"
eval "$cmd"

Mas desde que você está tentando passar argumentos com $@ , o que você precisa aqui é uma função

webgrep() {
    find /path/to/webpage -type f | grep -v .svn | xargs grep "$@"
}

Observe que isso terá problemas com qualquer caminho que inclua um caractere de espaço em branco. Ou padrões que começam com menos. E ele terá que escanear todo o conteúdo dos diretórios .svn antes de ignorá-los. E seria bom lidar com o usuário acidentalmente passando vários argumentos (por exemplo, porque o padrão não foi citado corretamente). Uma maneira melhor é

webgrep() {
    find /path/to/webpage -name .svn -prune -o -type f -exec grep -e "$*" {} +
}

Em seguida, ligue para ele assim

webgrep PATTERN
    
por 26.07.2012 / 23:35
0

Tente criar sua variável assim:

cmd=$(find /path/to/webpage -type f | grep -v .svn | xargs grep $@)

ou

cmd='find /path/to/webpage -type f | grep -v .svn | xargs grep $@'

Ou talvez um alias seja mais adequado:

alias cmd="find /path/to/webpage -type f | grep -v .svn | xargs grep $@"
    
por 26.07.2012 / 21:30
0

Não coloque código em uma variável, como regra geral. Use uma função.

cmd() { 
  find /path/to/webpage -type f |
    grep -v .svn                | 
    xargs grep $@
}

cmd "$@"

Se você eventualmente acabar tendo que colocar um código simples em uma variável (não, mas se você fizer ...) então como mencionado, você não pode embutir coisas como canos ou redirecionamentos sem um eval ... e não faça isso também, lol

Mas não o execute em backticks, que executam os comandos em um subshell e o substituem pela saída, para que a saída se torne o que o analisador está recebendo como o comando a ser executado:

$: 'echo foo'
bash: foo: command not found
    
por 28.11.2018 / 20:46

Tags