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"
} | sort
tai
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