Here Documents, Tutaj Dokumenty?
Here Documents, ten dziwnie brzmiący w rodzimym języku zlepek słów, jest rodzajem przekierowania, umożliwiającym potraktowanie części kodu źródłowego jak pliku. Kolejne linie tekstu przekazywane są na wejście innych poleceń, np. cat, ssh, ftp. Tak jakby były wpisywane linia po lini. Występuje w wielu powłokach Linux, miedzy innymi: bash, zsh, sh. Podobne konstrukcje można też spotkać w językach programowania wyższego poziomu, np. Perl. Heredocs domyślnie zachowuje białe znaki, dlatego jest używany do zapisywania wieloliniowych literałów.
Konstrukcja
Heredocs może zawierać tekst, polecenia i zmienne. Umieszczone są pomiędzy znacznikami. Znacznikiem może być dowolne słowo, często spotykane są słowa “EOF” i “END”. Dobrze jest dobierać słowa oddające charakter zawartej miedzy nimi treści, np. SQL, HTML.
Tak opakowany kod poprzedzony jest poleceniem i znakami przekierowania <<
. Opcjonalny parametr -
spowoduje zignorowanie białych znaków. Podobieństwo do występującego w wielu powłokach przekierowania strumienia danych nie jest przypadkowe. Heredocs formalnie jest przekierowaniem literałów znakowych, na standardowe wejście innych poleceń 1.
Budowa HereDocs:
POLECENIE <<[-] ZNACZNIK
"linia 1"
"linia 2"
"linia 3"
polecenie
$zmienna
.
.
.
ZNACZNIK
Przykłady
Na wejście polecenia SSH przekierowana jest lista poleceń, która wykonuje się na maszynie zdalnej:
$ ssh mate@192.168.0.2 bash << EOF
whoami
uptime
cat /etc/os-release
EOF
Wyjście:
mate
14:41:12 up 4 days, 1:05, 0 users, load average: 0.44, 0.28, 0.25
PRETTY_NAME="Ubuntu 22.04 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
Here string domyślnie pozwala na zachowanie białych znaków:
$ cat << "EOF"
foo
bar
baz
what
EOF
Wyjście:
foo
bar
baz
what
Opcjonalny parametr -
pozwala na zignorowanie białych znaków:
cat <<- "EOF"
foo
bar
baz
what
EOF
Wyjście:
foo
bar
baz
what
W bloku here można umieścić zmienne, zostaną one odpowiednio rozwinięte:
$ HOST="193.168.0.1"
$ ssh 192.168.0.2 bash << EOF
ping $HOST
EOF
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=1.53 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.743 ms
...
Heredoc można wykorzystać do budowania zapytań SQL:
$ read -r -d '' QUERY << SQL
SELECT *
FROM AdventureWorks2016.HumanResources.Department
WHERE GroupName = 'Manufacturing';
SQL
$ sqlcmd -S localhost -U SA -P passw04d -Q $QUERY
Na koniec bardziej praktyczny przykład
Ta wydawało by się, dziwna trochę konstrukcja, pozwala na użycie wieloliniowych literałów znakowych, tam gdzie jest to utrudnione. Okazuje się to przydatne np. przy budowaniu dłuższych zapytań SQL. Zamiast jednej długiej, nieczytelnej lini. Można sformatować za jej pomocą blok poleceń, umieścić go w zmiennej a następnie przesłać do serwera SQL. Pozwala to na zwiększenie czytelności pisanego skryptu i budowanie złożonych zapytań SQL.
Poniżej przykładowy listing skryptu, wykorzystującego opisane powyżej metody. Za pomocą heredocs budowane jest polecenie przywrócenia bazy danych z kopii zapasowej, na serwerze mssql umieszczonym w kontenerze docker.
/restore_db.sh
#!/usr/bin/env bash
source .env
DB_URL="https://github.com/Microsoft/"`
`"sql-server-samples/releases/download/"`
`"adventureworks/AdventureWorks2016.bak"
DB_NAME="AdventureWorks2016"
DB_PATH='"/var/opt/mssql/backup/AdventureWorks2016.bak"'
DB_DATA='"AdventureWorks2016_Data"'
DB_DATA_PATH='"/var/opt/mssql/data/AdventureWorks2016_Data.mdf"'
DB_LOG='"AdventureWorks2016_Log"'
DB_LOG_PATH='"/var/opt/mssql/data/AdventureWorks2016_Log.ldf"'
read -r -d '' QUERY<<SQL
RESTORE DATABASE $DB_NAME
FROM DISK = $DB_PATH
WITH MOVE $DB_DATA
TO $DB_DATA_PATH,
MOVE $DB_LOG
TO $DB_LOG_PATH;
SQL
docker exec -it mssql \
sh -c "wget --directory-prefix=/var/opt/mssql/backup/ $DB_URL"
docker exec -it mssql /opt/mssql-tools/bin/sqlcmd -S localhost \
-U SA -P $SA_PASSWORD \
-Q "$QUERY"
[1] Chapter 19. Here Documents - Advanced Bash Scripting Guide