printf "'%s\n'" "$x" printf '"%s\n"' "$x"
tai näin:
cat <<EOF '$x' "$x" EOF
echo \'\"'#'\$\\\'\<\>\| echo "'\"#\$\\\`<>|" echo \''"#$\`<>|'
Myös seuraava toimii mutta on vaarallinen:
echo "'\"#$\\\`<>|"Jos esim. $:n ja #:n järjestys vaihtuu, tuo ei enää toimi (miksei?).
Ilman echoa tämä kävisi myös esim. näin:
cat <<\EOF '"#$\`<>| EOF
ERR=$(date $f 2>&1 1>/dev/null)
Huomaa uudelleensuuntausten järjestys, ja että shellin mahdollinen virheilmoitus (esim. jos date ei ole PATHissa tai $f ei ole olemassa) tulostuu kuitenkin, vain date:n virheilmoitus otetaan kiinni.
Jos date:n varsinainen tulos halutaan päätteelle, voi /dev/nullin korvata /dev/tty:llä.
y=${x%"${x#??}"}; y=${y#?} y=${x#"${x%??}"}; y=${y%?}tai esim.
y=${x#?}; y=${y%"${y#?}"} y=${x%?}; y=${y#"${y%?}"}Lainausmerkit ovat tarpeen jos $x voi sisältää joitakin erikoismerkkejä (esim. '*--*'). (Miksi niitä ei tarvita enempää?)
mydirname() { printf "%s\n" "${1%/*}" ; } mybasename() { printf "%s\n" "${1##*/}" ; }
Huom. nuo eivät toimi aivan samoin kuin oikeat dirname ja basename, erityisesti jos argumentti päättyy kauttaviivaan, eivätkä ymmärrä basenamen toista argumenttia. Täydellisemmin (muttei aivan):
mydirname() { case "$1" in /|//) echo / ;; *?/?*) set -- "${1%/}" printf "%s\n" "${1%/*}" ;; /*) echo / ;; *) echo . ;; esac } mybasename() { case "$1" in /|//) echo / ;; *) set -- "${1%/}" "${2-}" set -- "${1##*/}" "$2" printf "%s\n" "${1%$2}" ;; esac }
Huomaa argumenttien arvon uudelleenasettelu (käyttö lokaaleina muuttujina) funktiossa. Missä tilanteissa nuo vielä käyttäytyvät eri tavoin kuin standardikomennot?
fun() { p=${PATH:-/usr/bin} ls -- "${p%%:*}" }
Huomaa toimivuus myös jos $PATH alkaa kaksoispisteellä (mikä siinä tarkoittaa oletushakemistoa) tai erikoismerkkejä sisältävällä nimellä.
for i in "$x" "$y" "$z" do printf "%s\n" "$i" done | sorttai
{ printf "%s\n" "$x" printf "%s\n" "$y" printf "%s\n" "$z" } | sorttai
printf "%s\n" "$x" "$y" "$z" | sorttai
sort <<EOF $x $y $z EOFHuomaa toiminta jos muuttujissa on useita peräkkäisiä välilyöntejä tai kenoviivoja (erityisesti \n tai \c).
Virhe() { printf "%s: %s\n" "${0##*/}" "${*-tuli virhe}" >&2 exit 1 }Tuo sallii myös tyhjän virheilmoituksen (jos kutsutaan esim. Virhe '' - bash 1.x ei toimi tässä oikein), ja vaikka virheilmoitus muodostuisi useasta sanasta (ts. Virhe nyt meni pieleen toimii ilman lainausmerkkejäkin). Skriptin nimestä poistetaan mahdollinen polku selvyyden vuoksi.
myls() { ( cd ${1:-.} printf "%s " * echo ) }Huomaa alishell (sulut), että alkuperäinen oletushakemisto ei vaihdu. Tuo ei toimi oikein jos hakemisto on tyhjä (tulostaa *:n), ei myöskään jos sen nimi alkaa viivalla tai sisältää tyhjiä. Parempi:
myls() { ( cd -- "${1:-.}" for i in * ;do [ -e "$i" ] && printf "%s " "$i" done && echo ) }Kumpikaan noista ei tulosta pistealkuisia tiedostoja, kuten ei ls:kaan. (Miten ne saisi siihen?)
Hakemistoja voi tutkia test-komennolla:
dir3() { [ -d "$1" ] && [ -x "$1" ] && [ -d "$2" ] && [ -x "$2" ] && [ -d "$3" ] && [ -x "$3" ] }tai kokeilla pääseekö niihin cd:llä (joka tuottaa virheilmoituksen stderriin statusarvon lisäksi):
dir3() { (cd "$1") && ([ -z "$2" ] || cd "$2") && ([ -z "$3" ] || cd "$3") }tai
dir3() { # cd / onnistuu aina! (cd "$1") && (cd "${2:-/}") && (cd "${3:-/}") }Seuraavat toimivat useammallakin kuin kolmella hakemistolla:
dirn() { for dir in "$@" ;do (cd "$dir") || return 1 done }
dirn() { while [ $# -ne 0 ] ;do [ -d "$1" ] && [ -x "$1" ] || return 1 shift done }
#! /usr/bin/sh # skripti1 eval a=\$$1 printf "%s\n%s\n" $1 "$a" >/tmp/MyTmp #! /usr/bin/sh # skripti2 { read var; value=$(cat) ; } </tmp/MyTmp eval new=\$$var printf "\$%s oli '%s', nyt se on '%s'\n" $var "$value" "$new"
Kahdella aputiedostolla saman voi tehdä ehkä selkeämmin:
#! /usr/bin/sh # skripti1 eval a=\$$1 printf "%s\n" $1 >/tmp/MyVar printf "%s\n" "$a" >/tmp/MyValue #! /usr/bin/sh # skripti2 read var </tmp/MyVar value=$(cat /tmp/MyValue) eval new=\$$var printf "\$%s oli '%s', nyt se on '%s'\n" $var "$value" "$new"
Jos erikoismerkeistä ei välitetä mitään, seuraavakin toimii (mitkä merkit rikkovat sen?):
#! /usr/bin/sh # skripti1 eval a=\$$1 echo "echo $1 oli '$a', nyt se on '\$$1'" >/tmp/MyTmp #! /usr/bin/sh # skripti2 . /tmp/MyTmp
Seuraava versio toimii jo paremmin (missä tilanteissa se ei toimi?):
#! /usr/bin/sh # skripti1 eval a=\$$1 cat >/tmp/MyTmp <<EOF read value <<'ThE eNd' $a ThE eNd printf "\$%s oli '%s', nyt se on '%s'\n" '$1' "\$value" "\$$1" EOF #! /usr/bin/sh # skripti2 . /tmp/MyTmp