J'essaie de comprendre comment faire en sorte que bash (force?) Développe des variables dans une chaîne (chargée à partir d'un fichier).
J'ai un fichier appelé "quelquechose.txt" avec le contenu:
hello $FOO world
Je cours ensuite
export FOO=42
echo $(cat something.txt)
cela retourne:
hello $FOO world
Il n'a pas développé $ FOO même si la variable a été définie. Je ne peux ni évaluer ni source le fichier - car il essaiera de l'exécuter (il n'est pas exécutable tel quel - je veux juste la chaîne avec les variables interpolées).
Des idées?
Je suis tombé sur ce que je pense être LA réponse à cette question: la commande envsubst
.
envsubst < something.txt
Si ce n'est pas déjà disponible dans votre distribution, c'est dans la
gettext
.@Rockallite - J'ai écrit un petit script wrapper pour résoudre le problème '\ $'.
(BTW, il existe une "fonctionnalité" de envsubst, expliquée à l'adresse https://unix.stackexchange.com/a/294400/7088 Pour développer uniquement certaines des variables de l'entrée mais je conviens que fuir les exceptions est beaucoup plus pratique.)
Voici mon script:
#! /bin/bash
## -*-Shell-Script-*-
CmdName=${0##*/}
Usage="usage: $CmdName runs envsubst, but allows '\$' to keep variables from
being expanded.
With option -sl '\$' keeps the back-slash.
Default is to replace '\$' with '$'
"
if [[ $1 = -h ]] ;then echo -e >&2 "$Usage" ; exit 1 ;fi
if [[ $1 = -sl ]] ;then sl='\' ; shift ;fi
sed 's/\\\$/\${EnVsUbDolR}/g' | EnVsUbDolR=$sl\$ envsubst "$@"
La plupart des réponses utilisent le type de travail eval
et echo
, mais ne tiennent pas compte de plusieurs choses, telles que plusieurs lignes, la tentative d'échapper aux méta-caractères de Shell, les échappements à l'intérieur du modèle ne devant pas être développés par bash, etc.
J'avais le même problème et j'ai écrit cette fonction Shell qui, autant que je sache, gère tout correctement. Cela ne supprimera toujours que les nouvelles lignes de sortie du modèle, en raison des règles de substitution de commande de bash, mais je n'ai jamais constaté que ce soit un problème tant que tout le reste reste intact.
apply_Shell_expansion() {
declare file="$1"
declare data=$(< "$file")
declare delimiter="__apply_Shell_expansion_delimiter__"
declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
eval "$command"
}
Par exemple, vous pouvez l'utiliser comme ceci avec un parameters.cfg
qui est en réalité un script Shell qui ne définit que des variables, et un template.txt
qui est un modèle qui utilise ces variables:
. parameters.cfg
printf "%s\n" "$(apply_Shell_expansion template.txt)" > result.txt
En pratique, je l’utilise comme une sorte de système de gabarit léger.
tu peux essayer
echo $(eval echo $(cat something.txt))
Vous ne voulez pas imprimer chaque ligne, vous voulez l’évaluer afin que Bash puisse effectuer des substitutions de variables.
FOO=42
while read; do
eval echo "$REPLY"
done < something.txt
Voir help eval
ou le manuel de Bash pour plus d'informations.
Une autre approche (qui semble dégueulasse, mais je le mets ici quand même):
Ecrivez le contenu de quelquechose.txt dans un fichier temporaire, avec une instruction echo intégrée:
something=$(cat something.txt)
echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out
puis source dans une variable:
RESULT=$(source temp.out)
et le $ RESULT aura tout élargi. Mais cela semble tellement faux!
Si vous souhaitez uniquement que les références de variable soient étendues (un objectif que j'ai moi-même), vous pouvez procéder comme suit.
contents="$(cat something.txt)"
echo $(eval echo \"$contents\")
(Les citations échappées autour de $ content est la clé ici)
Méthode bash
, (variante à une ligne de la réponse de Michael Neale ), en utilisant processus & substitution de commande:
FOO=42 . <(echo -e echo $(<something.txt))
Sortie:
hello 42 world
Notez que export
n'est pas nécessaire.
GNUsed
e
_ valuate méthode, si quelque chose.txt a plusieurs lignes:
FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
$ eval echo $(cat something.txt)
hello 42 world
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-Apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
foo=45
file=something.txt # in a file is written: Hello $foo world!
eval echo $(cat $file)
envsubst
est une excellente solution (voir la réponse de LenW) si le contenu que vous substituez est d'une longueur "raisonnable".
Dans mon cas, je devais remplacer le nom de la variable par le contenu d'un fichier. envsubst
nécessite que le contenu soit exporté en tant que variables d'environnement et bash a un problème lors de l'exportation de variables d'environnement de plus d'un mégaoctet ou plus.
Utilisation de la solution solution de cuonglm à partir d'une question différente:
needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.)
needle_file="doc1_base64.txt" # Will be substituted for the needle
haystack=$requestfile1 # File containing the needle
out=$requestfile2
awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out
Cette solution fonctionne même pour les gros fichiers.
Solution suivante:
permet de remplacer des variables définies
laisse des variables inchangées, des espaces réservés non définis. Ceci est particulièrement utile lors de déploiements automatisés.
prend en charge le remplacement de variables dans les formats suivants:
${var_NAME}
$var_NAME
indique quelles variables ne sont pas définies dans l'environnement et renvoie un code d'erreur pour de tels cas
TARGET_FILE=someFile.txt;
ERR_CNT=0;
for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do
VAR_VALUE=${!VARNAME};
VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' );
VAR_VALUE2=${!VARNAME2};
if [ "xxx" = "xxx$VAR_VALUE2" ]; then
echo "$VARNAME is undefined ";
ERR_CNT=$((ERR_CNT+1));
else
echo "replacing $VARNAME with $VAR_VALUE2" ;
sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE};
fi
done
if [ ${ERR_CNT} -gt 0 ]; then
echo "Found $ERR_CNT undefined environment variables";
exit 1
fi