2015-07-18 1 views
1

J'ai certaines variables dans un script bash qui peuvent contenir un nom de fichier ou être désactivées. Leur contenu devrait être passé comme un argument supplémentaire à un programme. Mais cela laisse un argument vide lorsque la variable n'est pas définie.omettre de passer un argument entre guillemets vide

$ afile=/dev/null 
$ anotherfile=/dev/null 
$ unset empty 
$ cat "$afile" "$empty" "$anotherfile" 
cat: : No such file or directory 

Sans guillemets, cela fonctionne très bien car l'argument supplémentaire est simplement omis. Mais comme les variables peuvent contenir des espaces, elles doivent être citées ici. Je comprends que je pourrais simplement envelopper la ligne entière dans un test sur la vacuité. Mais un test pour chaque variable conduirait à un arbre de décision énorme et alambiqué.

Existe-t-il une solution plus compacte à cela? Est-ce que bash peut omettre une variable vide citée?

Répondre

1

Vous peut utiliser un alternate value parameter expansion (${var+altvalue}) pour inclure la variable cité si elle est définie:

cat ${afile+"$afile"} ${empty+"$empty"} ${anotherfile+"$anotherfile"} 

Depuis les guillemets doubles sont dans la chaîne de valeur alternative (pas autour de t l'expression entière du paramètre), ils ne prennent effet que si la variable est définie. Notez que vous pouvez utiliser + (qui utilise la valeur alternative si la variable est définie) ou :+ (qui utilise la valeur alternative si la variable est définie ET non vide).

+0

Je pensais aux délices de l'expansion des paramètres, mais je ne pensais pas à la citation à l'intérieur des accolades. Très élégant, en effet, et donc exactement ce que je cherchais. – XZS

1

Essayez avec:

printf "%s\n%s\n%s\n" "$afile" "$empty" "$anotherfile" | egrep -v '^$' | tr '\n' '\0' | xargs -0 cat 
+0

Malheureusement, votre ligne me laisse la même erreur. Le simple fait d'utiliser xargs ne supprime pas encore les arguments vides. Insertion de 'sed 's/\ x0 \ x0/\ x0/g; s/\ x0 * $ //; s/^ \ x0 * //' |' avant que xargs puisse le faire. Avec cela ajouté, je l'accepterais comme la bonne réponse. – XZS

+0

En outre, la partie 'tr' peut être supprimée. 'printf' peut imprimer des valeurs nulles directement en remplaçant' \ n' dans la chaîne de format avec '\ 0'. – XZS

+0

@XZS: mise à jour en utilisant "grep" au lieu de "xargs -r". A propos de votre solution en utilisant sed, je vous suggère de l'afficher comme réponse indépendante (stackoverflow promeut les réponses, peu importe si l'auteur est l'auteur de la question). –

1

Une solution pure bash est possible en utilisant des tableaux. Alors que "$empty" va évaluer un argument vide, "${empty[@]}" s'étendra à tous les champs du tableau, qui sont, dans ce cas, aucun.

$ afile=(/dev/null) 
$ unset empty 
$ alsoempty=() 
$ cat "${afile[@]}" "${empty[@]}" "${alsoempty[@]}" 

Dans les situations où les tableaux ne sont pas une option, reportez-vous à la réponse plus polyvalente pasaba por aqui.

1

Dans le cas d'une commande comme cat où vous pouvez remplacer un argument vide avec un fichier vide, vous pouvez utiliser la syntaxe standard de remplacement par défaut du shell:

cat "${file1:-/dev/null}" "${file2:-/dev/null}" "${file3:-/dev/null}" 

Sinon, vous pouvez créer un flux de sortie concaténés des arguments qui existent, soit par la tuyauterie (voir ci-dessous), soit par substitution de processus:

{ [[ -n "$file1" ]] && cat "$file1"; 
    [[ -n "$file2" ]] && cat "$file2"; 
    [[ -n "$file3" ]] && cat "$file3"; } | awk ... 

Cela pourrait être simplifié avec une fonction d'utilité:

cat_if_named() { [[ -n "$1" ]] && cat "$1"; } 

Dans le cas particulier de cat pour construire un nouveau fichier, vous pouvez simplement faire une série que d'y ajouter:

# Start by emptying or creating the output file. 
. > output_file 
cat_if_named "$file1" >> output_file 
cat_if_named "$file2" >> output_file 
cat_if_named "$file3" >> output_file 

Si vous devez conserver les arguments individuels - par exemple, si vous voulez transmettre la liste à grep, qui imprimera le nom du fichier ainsi que les matchs - vous pouvez construire un tableau d'arguments, choisissant uniquement les arguments qui existent:

args=() 
[[ -n "$file1" ]] && args+=("$file1") 
[[ -n "$file2" ]] && args+=("$file2") 
[[ -n "$file3" ]] && args+=("$file3") 

avec 4 bash.3 ou mieux, vous pouvez utiliser un nameref pour faire une fonction d'utilité de faire ce qui précède, ce qui est certainement la solution la plus compacte et générale au problème:

non_empty() { 
    declare -n _args="$1" 
    _args=() 
    shift 
    for arg; do [[ -n "$arg" ]] && _args+=("$arg"); done 
} 

par exemple:

non_empty my_args "$file1" "$file2" "$file3" 
grep "$pattern" "${my_args[@]}"