Passando caminhos com espaços como argumentos

3

Estou tendo dificuldades em passar algumas variáveis de string com espaços nelas como argumentos para um programa.
Para depurar e mostrar os argumentos sendo passados, criei um script Python de demonstração -:

##### show_args.py #####

import sys

def main():
    # Display the arguments passed to the script
    print "Number of arguments =", len(sys.argv)
    for arg in sys.argv:
        print arg

if __name__ == '__main__':
    main()

Agora, o script demonstra o problema -:

path_with_spaces="$HOME/blah blah"
arg_list="$path_with_spaces/abc $path_with_spaces/xyz"

python show_args.py $arg_list

Saída -:

Number of arguments = 5
show_args.py
/home/anmol/blah
blah/abc
/home/anmol/blah
blah/xyz

O que eu realmente quero é isso -:

path_with_spaces="$HOME/blah blah"

python show_args.py "$path_with_spaces/abc" "$path_with_spaces/xyz"

Saída -:

Number of arguments = 3
show_args.py
/home/anmol/blah blah/abc
/home/anmol/blah blah/xyz

Para confirmar que o problema estava ocorrendo apenas para caminhos com espaços, criei o seguinte script -:

path_without_spaces="$HOME/blah"
arg_list="$path_without_spaces/abc $path_without_spaces/xyz"

python show_args.py $arg_list

Saída -:

Number of arguments = 3
show_args.py
/home/anmol/blah/abc
/home/anmol/blah/xyz

Enquanto pesquisava as soluções para este problema, encontrei esta resposta , de acordo com a qual a maneira correta é colocar os argumentos em uma variável de matriz em vez de uma variável de seqüência de caracteres.
O script mostrando essa nova abordagem -:

path_with_spaces="$HOME/blah blah"
arg_list=("$path_with_spaces/abc" "$path_with_spaces/xyz")

python show_args.py "${arg_list[@]}"

Saída -:

Number of arguments = 3
show_args.py
/home/anmol/blah blah/abc
/home/anmol/blah blah/xyz

Embora esta solução esteja funcionando corretamente, ainda quero saber se existe uma maneira de realizar a mesma coisa usando uma variável de string, em vez de uma variável de array.

Minha configuração do sistema -:

  • Ubuntu 14.04 LTS
  • Bash 4.3.11
  • Terminal Gnome 3.6.2
por Anmol Singh Jaggi 22.12.2015 / 19:08

2 respostas

4

Em:

path_with_spaces="$HOME/blah blah"
arg_list="$path_with_spaces/abc $path_with_spaces/xyz"

python show_args.py $arg_list

Você está usando uma variável escalar / string e usando o operador split + glob (deixando essa variável sem aspas) para dividir o conteúdo dessa variável e gerar os argumentos para passar para python .

A parte split do operador split + glob divide os caracteres armazenados no parâmetro $IFS special.

Aqui, você pode desativar a parte glob e dividir em um caractere não encontrado em seus caminhos. Por exemplo, se nova linha não for encontrado nos caminhos:

path_with_spaces="$HOME/blah blah"
arg_list="$path_with_spaces/abc
$path_with_spaces/xyz"
IFS="
"
set -f # disable glob.
python show_args.py $arg_list

Você também pode usar aspas do shell e usar eval para que o shell interprete essas aspas (aqui usando os recursos bash -specific):

printf -v arg_list '%q ' "$path_with_spaces/abc" \
                         "$path_with_spaces/xyz"

eval "python show_args.py $arg_list"
    
por 22.12.2015 / 20:04
2

Sim.

Você pode usar o separador de caminho padrão para separar caminhos. Existem dois tipos de caracteres que são ilegais em um nome de caminho:

  • o caractere /NUL

  • o separador de caminho $IFS

Quando isso acontece, você pode separar os nomes de caminho no separador com o parâmetro parâmetro de campo interno //NUL especial do shell. Quando dois ou mais separadores ocorrem em seqüência, o componente resultante será um argumento //* valued - que não pode ser um nome de caminho.

Para trabalhar, você deve fazer root diretamente em todos os caminhos (e descartar uma raiz $PWD ) , e você deve garantir que tenha caminhos canônicos - ou pelo menos que todos os cd tenham sido espremido para uma única ocorrência por (pontos não vão doer - ou ajudar) . Depois de ter feito isso:

paths='/path/one//path/t w o//p a t h/t h r e e/'
set -f --; IFS=/
for p in ${paths#/}$IFS
do  printf ${p:+/}%s\n "$*"
    set -- ${p:+"$@"/$p}
done
/path/one
/path/t w o
/p a t h/t h r e e

Você pode obter um caminho canônico em -P w / cd .

cd -- /some//../screwy/../path/to///destination
printf %s\n "$PWD"
/path/to/destination

Se você usar a $PWD switch -L garantirá que um caminho físico absoluto para o diretório de trabalho atual esteja definido como cd . Senão com %code% ou por padrão %code% manterá qualquer indireção implícita por mudanças anteriores em diretórios com links simbólicos à medida que sua sessão de shell progride.

E assim você pode coletar os nomes de uma árvore de forma mais confiável andando por ela.

    
por 22.12.2015 / 20:14