Transmettre des paths avec des espaces en tant qu'arguments

J'ai de la difficulté à transmettre des variables de string contenant des espaces en tant qu'arguments d'un programme.
Pour déboguer et montrer les arguments passés, j'ai créé un script Python de démonstration -:

##### 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() 

Maintenant, le script démontrant le problème -:

 path_with_spaces="$HOME/blah blah" arg_list="$path_with_spaces/abc $path_with_spaces/xyz" python show_args.py $arg_list 

Sortie -:

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

Ce que je veux en réalité est ceci:

 path_with_spaces="$HOME/blah blah" python show_args.py "$path_with_spaces/abc" "$path_with_spaces/xyz" 

Sortie -:

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

Pour confirmer que le problème ne se produisait que pour les paths contenant des espaces, j'ai créé le script suivant:

 path_without_spaces="$HOME/blah" arg_list="$path_without_spaces/abc $path_without_spaces/xyz" python show_args.py $arg_list 

Sortie -:

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

Lors de la search des solutions à ce problème, j'ai rencontré cette réponse selon laquelle la méthode correcte consiste à placer les arguments dans une variable de tableau plutôt que dans une variable de string.
Le script montrant cette nouvelle approche -:

 path_with_spaces="$HOME/blah blah" arg_list=("$path_with_spaces/abc" "$path_with_spaces/xyz") python show_args.py "${arg_list[@]}" 

Sortie -:

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

Bien que cette solution fonctionne correctement, je veux savoir s'il existe un moyen d'accomplir la même chose en utilisant une variable string plutôt qu'une variable tableau.

Ma configuration du système -:

  • Ubuntu 14.04 LTS
  • Bash 4.3.11
  • Terminal Gnome 3.6.2

Dans:

 path_with_spaces="$HOME/blah blah" arg_list="$path_with_spaces/abc $path_with_spaces/xyz" python show_args.py $arg_list 

Vous utilisez une variable scalaire / string et utilisez l'opérateur split + glob (en laissant cette variable sans guillemets) pour split le contenu de cette variable et générer les arguments à transmettre à python .

La partie dédoublée de l'opérateur split + glob se dédouble sur les caractères stockés dans le paramètre spécial $IFS .

Ici, vous pouvez désactiver la partie glob et partager sur un personnage introuvable dans vos paths. Par exemple, si newline n'est pas trouvé dans les paths:

 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 

Vous pouvez également utiliser la citation de shell et utiliser eval pour que le shell interprète ces guillemets (ici en utilisant des fonctionnalités spécifiques à bash ):

 printf -v arg_list '%q ' "$path_with_spaces/abc" \ "$path_with_spaces/xyz" eval "python show_args.py $arg_list" 

Oui.

Vous pouvez utiliser le séparateur de path par défaut pour séparer les paths. Il existe deux types de caractères qui sont illégaux dans un nom de path:

  • le caractère \0NUL

  • le séparateur / path

En l'occurrence, vous pouvez séparer les noms de path sur le séparateur avec le paramètre spécial $IFS Internal Field Separator $IFS du shell. Lorsque deux séparateurs ou plus se produisent en séquence, le composant résultant sera un argument de valeur \0NUL – qui ne peut pas être un nom de path.

Pour travailler, vous devez rooter tous les paths directement (et donc exclure une racine // ) , et vous devez vous assurer que vous avez des paths canoniques – ou au less que tous les //* ont été compressés à une seule occurrence par (les points ne seront pas blessé – ou aide) . Une fois que vous l'avez fait:


 paths='/path/one//path/two//path/three/' set -f --; IFS=/ for p in ${paths#/}$IFS do printf ${p:+/}%s\\n "$*" set -- ${p:+"$@"/$p} done 

 /path/one /path/two /path/three 

Vous pouvez get un path canonique dans $PWD w / cd .


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

 /path/to/destination 

Si vous utilisez le commutateur -P , cd garantira un path physique absolu vers le directory de travail en cours, défini sur $PWD . Sinon avec -L ou par défaut, cd maintiendra les indirections impliquées par les changements précédents dans les directorys symésortingques au fur et à mesure que votre session shell progresse.

Et ainsi, vous pouvez collecter les noms de manière fiable dans un tree en le promenant.