Próbuję dowiedzieć się, jak zrobić bash (wymusić?) Rozwinąć zmienne w ciągu (który został załadowany z pliku).
Mam plik o nazwie „coś.txt” z zawartością:
hello $FOO world
Potem biegnę
export FOO=42
echo $(cat something.txt)
to zwraca:
hello $FOO world
Nie rozszerzał $ FOO, mimo że zmienna była ustawiona. Nie mogę ewaluować ani pozyskiwać pliku - ponieważ spróbuje go wykonać (nie jest wykonywalnym, jak jest - chcę tylko ciąg znaków ze zmiennymi interpolowanymi).
Jakieś pomysły?
eval
.eval
i ważne jest, aby być świadomym konsekwencji.Odpowiedzi:
Natknąłem się na to, co uważam za odpowiedź na to pytanie:
envsubst
polecenie.Jeśli nie jest jeszcze dostępny w Twojej dystrybucji, znajduje się w
Pakiet GNU
gettext
.@Rockallite - Napisałem mały skrypt opakowujący, aby zająć się problemem „\ $”.
(BTW, istnieje "funkcja" envsubst, wyjaśniona na https://unix.stackexchange.com/a/294400/7088 w celu rozwijania tylko niektórych zmiennych wejściowych, ale zgadzam się, że unikanie wyjątków to znacznie więcej wygodna.)
Oto mój skrypt:
#! /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 "$@"
źródło
$
ucieczki znaku dolara ( ), npecho '\$USER' | envsubst
. Wyświetli bieżącą nazwę użytkownika z przedrostkiem ukośnika wstecznego, a nie$USER
.brew link --force gettext
Wiele odpowiedzi wykorzystuje
eval
iecho
rodzaj pracy, ale przerywa różne rzeczy, takie jak wiele wierszy, próba ucieczki z meta-znaków powłoki, ucieczki wewnątrz szablonu, które nie mają być rozszerzone przez bash itp.Miałem ten sam problem i napisałem tę funkcję powłoki, która o ile wiem, obsługuje wszystko poprawnie. To nadal usunie tylko końcowe znaki nowej linii z szablonu, z powodu reguł podstawiania poleceń basha, ale nigdy nie stwierdziłem, że jest to problem, o ile wszystko inne pozostaje nienaruszone.
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" }
Na przykład, możesz go użyć w ten sposób z,
parameters.cfg
który jest tak naprawdę skryptem powłoki, który po prostu ustawia zmienne, itemplate.txt
który jest szablonem, który używa tych zmiennych:. parameters.cfg printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
W praktyce używam tego jako pewnego rodzaju lekkiego systemu szablonów.
źródło
eval
pułapki. Spróbuj dodać; $(eval date)
na końcu dowolnej linii w pliku wejściowym, a uruchomidate
polecenie.możesz spróbować
echo $(eval echo $(cat something.txt))
źródło
echo -e "$(eval "echo -e \"`<something.txt`\"")"
jeśli chcesz zachować nowe linie.template.txt
jest to tekst. Ta operacja jest niebezpieczna, ponieważ wykonuje (ewaluuje) polecenia, jeśli są.Nie chcesz drukować każdego wiersza, chcesz go oszacować , aby Bash mógł wykonywać podstawienia zmiennych.
FOO=42 while read; do eval echo "$REPLY" done < something.txt
Aby
help eval
uzyskać więcej informacji, zobacz lub podręcznik Bash.źródło
eval
jest niebezpieczny ; zostaniesz nie tylko$varname
rozszerzony (tak jak w przypadkuenvsubst
), ale$(rm -rf ~)
także.Inne podejście (które wydaje się okropne, ale i tak je tutaj umieszczam):
Zapisz zawartość something.txt do pliku tymczasowego, z owiniętą instrukcją echo:
something=$(cat something.txt) echo "echo \"" > temp.out echo "$something" >> temp.out echo "\"" >> temp.out
następnie wróć do zmiennej:
RESULT=$(source temp.out)
a $ RESULT rozszerzy to wszystko. Ale wydaje się to takie złe!
źródło
Jeśli chcesz tylko rozszerzyć odwołania do zmiennych (cel, który miałem dla siebie), możesz wykonać poniższe czynności.
contents="$(cat something.txt)" echo $(eval echo \"$contents\")
(Kluczowe znaczenie mają tutaj cudzysłowy otaczające zawartość $)
źródło
eval "echo \"$$contents\""
i zachowane odstępyNastępujące rozwiązanie:
umożliwia podmianę zdefiniowanych zmiennych
pozostawia niezmienione zmienne zastępcze, które nie są zdefiniowane. Jest to szczególnie przydatne podczas zautomatyzowanych wdrożeń.
obsługuje zamianę zmiennych w następujących formatach:
${var_NAME}
$var_NAME
informuje, które zmienne nie są zdefiniowane w środowisku i zwraca kod błędu dla takich przypadków
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
źródło
Jeśli coś.txt ma tylko jedną linię,
bash
metodę (krótszą wersję „okropnej” odpowiedzi Michaela Neale'a ), używając podstawiania procesów i poleceń :FOO=42 . <(echo -e echo $(<something.txt))
Wynik:
Pamiętaj, że
export
nie jest to potrzebne.Jeśli something.txt ma jeden lub więcej wierszy, a GNU wyceny metodę:
sed
e
FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
źródło
sed
ocena najlepiej pasuje do mojego celu. Musiałem wyprodukować skrypty z szablonów, które miałyby wypełnione wartości ze zmiennych wyeksportowanych w innym pliku konfiguracyjnym. Obsługuje poprawnie ucieczkę w celu rozwinięcia poleceń, np. Umieszczenie\$(date)
w szablonie, aby uzyskać$(date)
wynik. Dzięki!$ 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.
źródło
foo=45 file=something.txt # in a file is written: Hello $foo world! eval echo $(cat $file)
źródło
envsubst
jest świetnym rozwiązaniem (patrz odpowiedź LenW), jeśli zastępowana treść ma „rozsądną” długość.W moim przypadku potrzebowałem podstawić w zawartości pliku, aby zastąpić nazwę zmiennej.
envsubst
wymaga, aby zawartość została wyeksportowana jako zmienne środowiskowe, a bash ma problem podczas eksportowania zmiennych środowiskowych, które są większe niż jeden megabajt.awk rozwiązanie
Korzystanie z rozwiązania Cuonglm z innego pytania:
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
To rozwiązanie działa nawet w przypadku dużych plików.
źródło
Następujące prace:
bash -c "echo \"$(cat something.txt)"\"
źródło
$foo
, ale niebezpieczne, takie jak$(rm -rf ~)
.