Jako správce systému jsou shelly součástí každodenních operací. Shelly často poskytují více možností a flexibility než grafické uživatelské rozhraní (GUI). Denně se opakující úkoly lze snadno automatizovat pomocí skriptů nebo lze úkoly naplánovat tak, aby se spouštěly v určitou dobu během dne. Shell poskytuje pohodlný způsob interakce se systémem a umožňuje vám udělat více za kratší dobu. Existuje mnoho různých shellů, včetně Bash, zsh, tcsh a PowerShell.
V tomto dvoudílném blogovém příspěvku sdílím některé z Bashových jednorázových vložek, které používám k urychlení mé práce a ponechání více času na pití kávy. V tomto úvodním příspěvku se budu věnovat historii, posledním argumentům, práci se soubory a adresáři, čtení obsahu souborů a funkcím Bash. Ve druhé části prozkoumám proměnné shellu, příkaz find, deskriptory souborů a vzdálené provádění operací.
Použijte příkaz historie
history
příkaz je šikovný. History
umožňuje vidět, jaké příkazy jsem spustil na konkrétním systému nebo jaké argumenty byly tomuto příkazu předány. Používám history
znovu spustit příkazy, aniž byste si museli něco pamatovat.
Záznam posledních příkazů je standardně uložen v ~/.bash_history.
Toto umístění lze změnit úpravou proměnné shellu HISTFILE. Existují další proměnné, jako je HISTSIZE (řádky k uložení do paměti pro aktuální relaci) a HISTFILESIZE (kolik řádků zachovat v souboru historie). Pokud se chcete dozvědět více o history
, viz man bash
.
Řekněme, že spustím následující příkaz:
$> sudo systemctl status sshd
Bash mi říká, že služba sshd neběží, takže další věc, kterou chci udělat, je spustit službu. Zkontroloval jsem jeho stav pomocí předchozího příkazu. Tento příkaz byl uložen v history
, takže na to mohu odkazovat. Jednoduše spustím:
$> !!:s/status/start/
sudo systemctl start sshd
Výše uvedený výraz má následující obsah:
- !! - opakujte poslední příkaz z historie
- :s/status/start/ – nahraďte stav s start
Výsledkem je spuštění služby sshd.
Dále zvýším výchozí hodnotu HISTSIZE z 500 na 5000 pomocí následujícího příkazu:
$> echo “HISTSIZE=5000” >> ~/.bashrc && source ~/.bashrc
Co když chci zobrazit poslední tři příkazy v historii? Zadávám:
$> history 3
1002 ls
1003 tail audit.log
1004 history 3
Spouštím tail
na audit.log
odkazem na číslo řádku historie. V tomto případě používám řádek 1003:
$> !1003
tail audit.log
..
..
Představte si, že jste něco zkopírovali z jiného terminálu nebo vašeho prohlížeče a omylem vložíte kopii (kterou máte ve vyrovnávací paměti kopírování) do terminálu. Tyto řádky budou uloženy v historii, což je něco, co nechcete. Takže tam zrušte nastavení HISTFILE &&exit přijde vhod
$> unset HISTFILE && exit
nebo
$> kill -9 $$
Odkaz na poslední argument předchozího příkazu
Když chci vypsat obsah adresáře pro různé adresáře, mohu mezi adresáři poměrně často přecházet. Existuje pěkný trik, který můžete použít k odkazování na poslední argument předchozího příkazu. Například:
$> pwd
/home/username/
$> ls some/very/long/path/to/some/directory
foo-file bar-file baz-file
Ve výše uvedeném příkladu /some/very/long/path/to/some/directory
je posledním argumentem předchozího příkazu.
Pokud chci cd
(změnit adresář) do tohoto umístění, zadám něco takového:
$> cd $_
$> pwd
/home/username/some/very/long/path/to/some/directory
Nyní jednoduše použijte pomlčku, abyste se vrátili tam, kde jsem byl:
$> cd -
$> pwd
/home/username/
Práce se soubory a adresáři
Představte si, že chci vytvořit adresářovou strukturu a do těchto adresářů přesunout hromadu souborů s různými příponami.
Nejprve vytvořím adresáře najednou:
$> mkdir -v dir_{rpm,txt,zip,pdf}
mkdir: created directory 'dir_rpm'
mkdir: created directory 'dir_txt'
mkdir: created directory 'dir_zip'
mkdir: created directory 'dir_pdf'
Dále přesunu soubory na základě přípony souboru do každého adresáře:
$> mv -- *.rpm dir_rpm/
$> mv -- *.pdf dir_pdf/
$> mv -- *.txt dir_txt/
$> mv -- *.zip dir_txt/
Dvojité pomlčky --
střední konec možností. Tento příznak zabraňuje tomu, aby byly soubory, které začínají pomlčkou, považovány za argumenty.
Dále chci nahradit/přesunout všechny soubory *.txt do souborů *.log, takže zadám:
$> for f in ./*.txt; do mv -v ”$file” ”${file%.*}.log”; done
renamed './file10.txt' -> './file10.log'
renamed './file1.txt' -> './file1.log'
renamed './file2.txt' -> './file2.log'
renamed './file3.txt' -> './file3.log'
renamed './file4.txt' -> './file4.log'
Místo použití for
smyčky výše, mohu nainstalovat prename
příkaz a dosáhnout výše uvedeného cíle takto:
$> prename -v 's/.txt/.log/' *.txt
file10.txt -> file10.log
file1.txt -> file1.log
file2.txt -> file2.log
file3.txt -> file3.log
file4.txt -> file4.log
Často při úpravě konfiguračního souboru vytvořím záložní kopii původního pomocí příkazu pro základní kopírování. Například:
$> cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.back
Jak vidíte, opakování celé cesty a připojení .back k souboru není tak efektivní a pravděpodobně náchylné k chybám. Existuje kratší a přehlednější způsob, jak to udělat. Tady to je:
$> cp /etc/sysconfig/network-scripts/ifcfg-eth0{,.back}
Můžete provádět různé kontroly souborů nebo proměnných. Spusťte help test
pro více informací.
Chcete-li zjistit, zda je soubor symbolickým odkazem, použijte následující příkaz:
$> [[ -L /path/to/file ]] && echo “File is a symlink”
Zde je problém, na který jsem nedávno narazil. Chtěl jsem gunzip/rozbalit spoustu souborů najednou. Bez přemýšlení jsem napsal:
$> tar zxvf *.gz
Výsledek byl:
tar: openvpn.tar.gz: Not found in archive
tar: Exiting with failure status due to previous errors
Soubory tar byly:
iptables.tar.gz
openvpn.tar.gz
…..
Proč to nefungovalo a proč by ls -l *.gz
místo toho pracovat? Pod kapotou to vypadá takto:
$> tar zxvf *.gz
Transformuje se následovně:
$> tar zxvf iptables.tar.gz openvpn.tar.gz
tar: openvpn.tar.gz: Not found in archive
tar: Exiting with failure status due to previous errors
tar
očekává se, že příkaz najde openvpn.tar.gz v rámci iptables.tar.gz. Vyřešil jsem to jednoduchým for
smyčka:
$> for f in ./*.gz; do tar zxvf "$f"; done
iptables.log
openvpn.log
Mohu dokonce generovat náhodná hesla pomocí Bash! Zde je příklad:
$> alphanum=( {a..z} {A..Z} {0..9} ); for((i=0;i<=${#alphanum[@]};i++)); do printf '%s' "${alphanum[@]:$((RANDOM%255)):1}"; done; echo
Zde je příklad, který používá OpenSSL:
$> openssl rand -base64 12
JdDcLJEAkbcZfDYQ
Čtení souboru řádek po řádku
Předpokládejme, že mám soubor s mnoha IP adresami a chci na těchto IP adresách pracovat. Například chci spustit dig
k načtení reverzních informací DNS pro adresy IP uvedené v souboru. Chci také přeskočit IP adresy, které začínají komentářem (# nebo hashtag).
Jako příklad použiji soubor A. Jeho obsah je:
10.10.12.13 some ip in dc1
10.10.12.14 another ip in dc2
#10.10.12.15 not used IP
10.10.12.16 another IP
Mohl bych zkopírovat a vložit každou IP adresu a poté spustit dig
ručně:
$> dig +short -x 10.10.12.13
Nebo bych mohl udělat toto:
$> while read -r ip _; do [[ $ip == \#* ]] && continue; dig +short -x "$ip"; done < ipfile
Co když chci zaměnit sloupce v souboru A? Například chci umístit IP adresy do sloupce úplně vpravo, aby souborA vypadal takto:
some ip in dc1 10.10.12.13
another ip in dc2 10.10.12.14
not used IP #10.10.12.15
another IP 10.10.12.16
Běžím:
$> while read -r ip rest; do printf '%s %s\n' "$rest" "$ip"; done < fileA
Používejte funkce Bash
Funkce v Bash se liší od funkcí napsaných v Pythonu, C, awk nebo jiných jazycích. V Bash by jednoduchá funkce, která akceptuje jeden argument a vypíše „Hello world“, vypadala takto:
func() { local arg=”$1”; echo “$arg” ; }
Funkci mohu zavolat takto:
$> func foo
Někdy se funkce vyvolá rekurzivně, aby provedla určitý úkol. Například:
func() { local arg="$@"; echo "$arg"; f "$arg"; }; f foo bar
Tato rekurze bude běžet navždy a bude využívat spoustu zdrojů. V Bash můžete použít FUNCNEST k omezení rekurze. V následujícím příkladu jsem nastavil FUNCNEST=5, abych omezil rekurzi na pět.
func() { local arg="$@"; echo "$arg"; FUNCNEST=5; f "$arg"; }; f foo bar
foo bar
foo bar
foo bar
foo bar
foo bar
bash: f: maximum function nesting level exceeded (5)
Použijte funkci k načtení nejnovějšího nebo nejstaršího souboru
Zde je ukázková funkce pro zobrazení nejnovějšího souboru v určitém adresáři:
latest_file()
{
local f latest
for f in "${1:-.}"/*
do
[[ $f -nt $latest ]] && latest="$f"
done
printf '%s\n' "$latest"
}
Tato funkce zobrazí nejstarší soubor v určitém adresáři:
oldest_file()
{
local f oldest
for file in "${1:-.}"/*
do
[[ -z $oldest || $f -ot $oldest ]] && oldest="$f"
done
printf '%s\n' "$oldest"
}
Toto je jen několik příkladů, jak používat funkce v Bash bez vyvolání dalších externích příkazů.
Někdy se přistihnu, jak znovu a znovu zadávám příkaz se spoustou parametrů. Jeden příkaz, který často používám, je kubectl
(Kubernetes CLI). Už mě nebaví spouštět tento dlouhý příkaz! Zde je původní příkaz:
$> kubectl -n my_namespace get pods
nebo
$> kubectl -n my_namespace get rc,services
Tato syntaxe vyžaduje, abych ručně zahrnul -n my_namespace
pokaždé, když spustím příkaz. Existuje jednodušší způsob, jak to udělat pomocí funkce:
$> kubectl () { command kubectl -n my_namespace ”$@” ; }
Nyní mohu spustit kubectl
aniž byste museli zadávat -n namespace
pokaždé:
$> kubectl get pods
Mohu použít stejnou techniku na další příkazy.
Sbalit
Toto je jen několik vynikajících triků, které pro Bash existují. Ve druhé části ukážu několik dalších příkladů, včetně použití funkce find a vzdáleného provádění. Doporučuji vám procvičit si tyto triky, aby byly vaše úkoly správy příkazového řádku jednodušší a přesnější.
[ Bezplatný online kurz:Technický přehled Red Hat Enterprise Linux. ]