Bashలో $() vs backticks: subshell, cd మరియు variables
Bashలో $() output చివరి newlineలను తొలగిస్తుంది, subshellలో cd మరియు variables మార్పులు నిలవవు. backticks nesting ఎందుకు కష్టమో, process substitution ఎప్పుడు ఉపయోగించాలో తెలుసుకోండి.
Bash command substitution ఏమి చేస్తుంది
Bash command substitution, ఆ command standard output కు ముద్రించిన text తో $(command) ను భర్తీ చేస్తుంది. Backticks తో రాసే పాత syntax కూడా ఇదే పని చేస్తుంది. చాలామందికి ఆశ్చర్యం కలిగించే ప్రవర్తన అంతా రెండు విషయాల వల్లే జరుగుతుంది: command subshell అనే వేరు process లో నడుస్తుంది, అలాగే దాని output చివర ఉన్న ప్రతి newline తొలగించబడుతుంది.
mkdir -p /tmp/subst-demo
cd /tmp/subst-demo
printf 'alpha\nbeta\ngamma\n' > three.txt
count=$(wc -l < three.txt)
echo "$count"3ఇదే మొత్తం feature. wc -l, newline తో పాటు 3 ను ముద్రించింది. ఆ newline తొలగించబడింది, అందువల్ల మీరు కోరుకున్న రెండు characters count లో నిల్వయ్యాయి. GNU wc standard input ను చదువుతున్నప్పుడు ముద్రించడానికి filename ఏదీ లేకపోవడంతో wc -l < three.txt కేవలం సంఖ్యను మాత్రమే ముద్రిస్తుందని గమనించండి. బదులుగా wc -l three.txt రాస్తే 3 three.txt capture అవుతుంది. అది వేరే string, తరువాత arithmetic విఫలమయ్యే సాధారణ కారణం.
ఈ guide లోని మిగతా భాగం సాధారణంగా ఎవరూ ఊహించని behavior గురించి ఉంటుంది. కారణం, subshell ఒక separate process. Separate process మీరు command type చేస్తున్న shell ను మార్చలదు.
$() ఉపయోగించండి; backticks వాడటం ఆపండి
రెండు రూపాలూ చెల్లుబాటు అవుతాయి. $() POSIX లో ఉంది. అందువల్ల dash, ash, busybox sh అన్నీ దీనికి మద్దతు ఇస్తాయి. ఇక backticks వాడటానికి portability కారణం లేదు. వాటిని ఉపయోగించకూడదనడానికి రెండు స్పష్టమైన కారణాలు ఉన్నాయి.
Backticks ను nest చేయలేరు
echo "$(echo "$(echo hi)")"
echo "`echo `echo hi``"hi
echo hiరెండో పంక్తి echo hi అనే అక్షరాలను యథాతథంగా ముద్రించింది. Shell తదుపరి unescaped backtick వరకు ముందుకు scan చేస్తుంది. కాబట్టి మీరు టైప్ చేసిన రెండో backtick, మొదటి backtick ను మూసివేసింది. వాస్తవంగా నడిచిన command ఎలాంటి arguments లేకుండా echo. అది ఖాళీ పంక్తిని ముద్రించింది. ఆ తర్వాత newline తొలగించబడటంతో అది పూర్తిగా ఖాళీగా మారింది. echo hi అనే పదాలు plain text గా మిగిలిపోయాయి. చివరలో ఉన్న backticks జత ఒక ఖాళీ command ను నడిపింది.
Backticks ను nest చేయాలంటే ప్రతి inner backtick ను escape చేయాలి:
echo "`echo \`echo hi\``"hiప్రతి అదనపు nesting స్థాయిలో escaping మళ్లీ రెండింతలు అవుతుంది. $() కు ఈ సమస్య ఏదీ లేదు. ఎందుకంటే parser delimiter character కోసం scan చేయకుండా parentheses ను సరిపోల్చుతుంది.
Command నడవకముందే Backticks మీ backslashes ను మార్చుతాయి
echo "$(echo 'a\\b')"
echo "`echo 'a\\b'`"a\\b
a\bఅదే inner command భిన్నమైన output ను ఇచ్చింది. Backticks లో shell, inner text ను parse చేయడానికి ముందే backslash escaping యొక్క ఒక పొరను తొలగిస్తుంది. అందువల్ల single quotes ఏదీ protect చేయలేదు. $() లో parentheses మధ్య ఉన్న text ఒక సాధారణ script లాగా parse అవుతుంది. కాబట్టి single quotes మీరు ఆశించిన విధంగానే పనిచేస్తాయి. ఈ సమస్య sed మరియు awk one-liners లో ఎక్కువగా కనిపిస్తుంది. అక్కడ తొలగిపోయిన backslash, పనిచేసే pattern ను ఎటువంటి స్పష్టమైన error లేకుండా వేరే pattern గా మార్చుతుంది.
$() లోపల quoting మళ్లీ మొదలవుతుంది. అందువల్ల double quotes ను escape చేయకుండానే nest చేయవచ్చు:
path=/etc/nginx/nginx.conf
echo "$(dirname "$path")"/etc/nginxBacktick సమాన రూపంలో $path చుట్టూ \" అవసరం. ప్రతి escape ఒక తప్పు చేసే అవకాశాన్ని సృష్టిస్తుంది.
$() లోని cd మీ shell ను ఎందుకు మార్చదు
ఎందుకంటే $(...) కొత్త process ను fork చేస్తుంది. Subshell మీ variables యొక్క ఒక copy మరియు మీ working directory యొక్క ఒక copy పొందుతుంది. అది తన copy ని మార్చి, ఏదో output ను print చేసి, exit అవుతుంది. ఆ copy కూడా దానితోనే తొలగిపోతుంది.
cd /tmp/subst-demo
pwd
target=$(cd /etc && pwd)
echo "$target"
pwd/tmp/subst-demo
/etc
/tmp/subst-demoఏదీ విఫలం కాలేదు. cd సరిగ్గా పనిచేసింది. Subshell లోని pwd నిజంగానే /etc ను print చేసింది. అయితే ఆ మార్పు తిరిగి రావడానికి మార్గం లేదు. Subshell తన parent కు standard output మరియు exit status మాత్రమే అందిస్తుంది.
Variable assignments కూడా ఇదే విధంగా పనిచేస్తాయి:
count=0
msg=$(count=99; echo "inside: $count")
echo "$msg"
echo "outside: $count"inside: 99
outside: 0ఇదే నియమం వల్ల మరింత తరచుగా కనిపించే ఈ bug సంభవిస్తుంది. ఇందులో substitution కు బదులుగా pipeline ఉంటుంది:
n=0
cat three.txt | while read -r line; do n=$((n+1)); done
echo "$n"0Pipeline లోని ప్రతి stage తన స్వంత subshell లో నడుస్తుంది. అందువల్ల while loop, n యొక్క ఒక copy ని increment చేసి, తరువాత exit అయింది. Pipe స్థానంలో redirect ఉపయోగిస్తే loop మీ shell లోనే నడుస్తుంది:
n=0
while read -r line; do n=$((n+1)); done < three.txt
echo "$n"3Bash, shopt -s lastpipe ఉపయోగించినప్పుడు pipeline లోని చివరి stage ను ప్రస్తుత shell లో నడపగలదు. అయితే ఇది job control off లో ఉన్నప్పుడు మాత్రమే సాధ్యం. Interactive shell లో ఇది ఎప్పుడూ నిజం కాదు. Redirect ఉపయోగించండి.
ప్రాంప్ట్ ఎక్కడికి పోయింది? $() లోని ఇంటరాక్టివ్ కమాండ్లు
Command substitution ప్రామాణిక అవుట్పుట్ను pipe లోకి మళ్లిస్తుంది. ప్రామాణిక ఇన్పుట్ను మాత్రం అలాగే ఉంచుతుంది. ఒక ప్రోగ్రామ్ తన ప్రశ్నను ప్రామాణిక అవుట్పుట్కు రాసి, తరువాత సమాధానం కోసం వేచి ఉంటే ప్రశ్న కనిపించదు. వేచి ఉండటం మాత్రం కొనసాగుతుంది. Terminal నిలిచిపోయినట్లు కనిపిస్తుంది.
ask() { printf 'Username: '; read -r u; printf '%s\n' "$u"; }
askUsername: deploy
deployమొదటి పంక్తిలోని deploy మీరు టైప్ చేసినది. ఇప్పుడు అదే function ను substitution లో అమలు చేయండి:
v=$(ask)deployమీరు టైప్ చేసిన అక్షరాలు మాత్రమే కనిపిస్తాయి. వాటిని ప్రోగ్రామ్ కాదు, terminal driver echo చేస్తుంది. ప్రాంప్ట్ వేరే చోటికి వెళ్లింది:
echo "$v"Username: deployఇప్పుడు ప్రాంప్ట్ variable లోపల, సమాధానానికి జతచేయబడి ఉంది. ఎందుకంటే function ప్రామాణిక అవుట్పుట్కు రాసిన ప్రతిదాన్ని $() capture చేసింది. మీ typing మాత్రం read కు చేరింది. ఎందుకంటే ప్రామాణిక ఇన్పుట్ను అసలు మార్చలేదు. "నా script ఏమీ print చేయకుండా hang అవుతోంది" అనే bug report కు ఇది ఖచ్చితమైన లక్షణం.
కొన్ని tools తమ ప్రాంప్ట్లను standard error కు లేదా నేరుగా /dev/tty కు రాస్తాయి. అందువల్ల ఈ సమస్య ఉండదు. కానీ చాలా tools అలా చేయవు. ఒక function substitution లోనే ఉండాల్సి వస్తే, దాని ప్రాంప్ట్ను మీరే standard error కు పంపండి:
ask() { printf 'Username: ' >&2; read -r u; printf '%s\n' "$u"; }
v=$(ask)
echo "$v"Username: deploy
deployStandard error capture చేయబడదు. అందువల్ల ప్రాంప్ట్ మీ terminal కు చేరుతుంది. సమాధానం మాత్రమే v లోకి వెళ్తుంది.
$() లో ls ఎందుకు భిన్నంగా కనిపిస్తుంది?
ఎందుకంటే ls, file descriptor 1 పై isatty ను పిలిచి, దాని సమాధానం ఆధారంగా output format ను మారుస్తుంది. Prompt వద్ద ఆ descriptor మీ terminal ను సూచిస్తుంది. అందువల్ల ls పేర్లను ఒకే లైన్లో columns గా అమర్చుతుంది. Substitution లో అది pipe అవుతుంది. కాబట్టి ls ప్రతి లైన్లో ఒక పేరును చూపిస్తుంది.
mkdir -p /tmp/tty-demo
cd /tmp/tty-demo
touch alpha beta delta gamma
echo "$(ls)"alpha
beta
delta
gammaఅదే తనిఖీ grep --color=auto లో colour ను, git లో pager ను ఆపివేస్తుంది. ఇది ఒక feature. Script కు అదనపు ఎంపిక అవసరం లేకుండానే స్థిరమైన machine-readable output లభిస్తుంది.
Pipelines గురించి తరచుగా అడిగే ప్రశ్నకు కూడా ఇది సమాధానం ఇస్తుంది. Prompt వద్ద ls | sort మరియు $(ls | sort) ఒకే విషయాన్ని print చేస్తాయి. రెండు సందర్భాల్లోనూ ls output పై pipe ను కలిగి ఉంటుంది. Substitution లో మారేది pipeline లోని చివరి దశ. sort ఎప్పుడూ terminal ఉందో లేదో తనిఖీ చేయదు. అందువల్ల దాని output మారదు. Pipeline చివర terminal-aware command ను ఉంచితే output మారుతుంది. అందుకే కళ్లతో పరిశీలించి పరీక్షించిన pipeline ను $() లో చుట్టిన వెంటనే భిన్నంగా ప్రవర్తించవచ్చు.
దీని నుంచి వచ్చే ఒక హెచ్చరిక: సౌకర్యంగా కనిపించినప్పటికీ script లో ls output ను parse చేయవద్దు. Filenames లో spaces మరియు newlines ఉండవచ్చు. Glob ను ఉపయోగించండి లేదా find -print0 తో read -d '' ను ఉపయోగించండి.
అవుట్పుట్ చివర నిశ్శబ్దంగా తొలగిపోయే newlineలు
Command substitution అవుట్పుట్ చివర ఉన్న ప్రతి newlineను తొలగిస్తుంది. చివరి newline మాత్రమే కాదు. అన్నింటినీ తొలగిస్తుంది.
cd /tmp/subst-demo
printf 'hello\n\n\n' > blanks.txt
wc -c < blanks.txt
v=$(cat blanks.txt)
printf '%s' "$v" | wc -c8
5ఆ ఫైల్లో hello తో పాటు మూడు newlineలు ఉన్నాయి, కాబట్టి మొత్తం 8 bytes. variableలో hello ఉంది, కాబట్టి మొత్తం 5 bytes. ఎలాంటి హెచ్చరిక లేకుండా మూడు bytes మాయమయ్యాయి.
ఈ తొలగింపు ఉద్దేశపూర్వకంగా జరుగుతుంది, సాధారణంగా ఉపయోగకరంగా కూడా ఉంటుంది. దీని వల్ల stamp=$(date -u +%Y%m%dT%H%M%SZ) line break ఉన్న పేరు కాకుండా ఉపయోగించగల filename భాగాన్ని తయారు చేస్తుంది. అందుకే ఈ విధానం షెడ్యూల్ చేసిన restic backup script వంటి సందర్భాల్లో సురక్షితం:
stamp=$(date -u +%Y%m%dT%H%M%SZ)
printf 'backup-%s.tar.gz\n' "$stamp"backup-20260804T031500Z.tar.gzమీ timestamp భిన్నంగా ఉంటుంది. పేరు ఒకే lineలో ఉండటం ముఖ్యం.
అయితే $() ను ఉపయోగించి fileలోని ఖచ్చితమైన bytesను తరలించలేరు. చివరి newlineలు అవసరమైతే, substitutionలో sentinel characterను జోడించి, తరువాత దాన్ని తొలగించండి:
v=$(cat blanks.txt; printf x)
v=${v%x}
printf '%s' "$v" | wc -c8x newlineల తరువాత ఉంటుంది. అందువల్ల తొలగించాల్సిన trailing newlineలు ఏవీ మిగలవు. తరువాత ${v%x} sentinelను తొలగించి, అసలు bytesను అలాగే ఉంచుతుంది.
ఇంకా రెండు సంబంధిత విషయాలు ఉన్నాయి. మొత్తం fileను చదవడానికి v=$(<blanks.txt), cat ను అమలు చేయకుండా ఇదే పని చేస్తుంది, ఎందుకంటే bash fileను స్వయంగా తెరుస్తుంది. ఇది కూడా trailing newlineలను అదే విధంగా తొలగిస్తుంది. అయితే here-stringలు విరుద్ధంగా పనిచేస్తాయి: మీరు రాయకపోయిన newlineను అవి జోడిస్తాయి:
wc -c <<< 'abc'4ఫలితాన్ని quote చేయండి; లేకపోతే bash దాన్ని split చేసి glob చేస్తుంది
Quote చేయని substitution ముందుగా word splitting, తరువాత pathname expansion గుండా వెళ్తుంది. Quote చేసిన substitution ఈ రెండింటినీ దాటవేస్తుంది.
printf 'a b\tc\nd\n' > words.txt
echo $(cat words.txt)
echo "$(cat words.txt)"a b c d
a b c
dQuote చేయకపోతే, IFS లోని అక్షరాల ఆధారంగా bash output ను split చేసింది. డిఫాల్ట్గా అవి space, tab మరియు newline. తరువాత echo ఆ నాలుగు భాగాలను single spaces తో కలిపింది. Quote చేసినప్పుడు, tab మరియు మధ్యలోని newline అలాగే ఉండి, మొత్తం text ఒకే word గా వచ్చింది.
Globbing మరింత ప్రమాదకరమైన భాగం:
mkdir -p /tmp/glob-demo
cd /tmp/glob-demo
touch one.txt two.txt
printf '*\n' > pattern.txt
p=$(cat pattern.txt)
echo $p
echo "$p"one.txt pattern.txt two.txt
*Assignment స్వయంగా సురక్షితంగానే ఉంది, ఎందుకంటే assignments పై word splitting లేదా globbing జరగదు. నష్టం echo $p వద్ద జరిగింది. అక్కడ * ప్రస్తుత directory కు సరిపడేలా expand అయింది. Config file నుంచి pattern చదివి quotes మర్చిపోయే script, తనకు కనిపించే ప్రతి file పై సులభంగా చర్య తీసుకుంటుంది. ప్రతి expansion ను quote చేస్తే ఈ తరహా bug పూర్తిగా తొలగిపోతుంది. Splitting నిజంగా కావాలనుకున్నప్పుడు మాత్రమే quotes వదిలేయండి; అలాంటి సందర్భాలు చాలా అరుదు.
local x=$(cmd) ఎల్లప్పుడూ 0 ఎందుకు తిరిగి ఇస్తుంది?
ఎందుకంటే local స్వయంగా ఒక command, మరియు $? substitution కలిగిన command స్థితిని కాకుండా local స్థితిని నివేదిస్తుంది.
check_bad() { local out=$(false); echo "status: $?"; }
check_badstatus: 0false 1తో ముగిసింది. local variable declaration ను విజయవంతంగా పూర్తి చేసింది. అందువల్ల 1 discard అయింది. declare, export, typeset మరియు readonly కూడా ఇదే విధంగా పనిచేస్తాయి. set -e కూడా దీన్ని గుర్తించదు, ఎందుకంటే shell దృష్టిలో ఏదీ విఫలం కాలేదు.
Declaration ను assignment నుంచి వేరు చేయండి:
check_good() { local out; out=$(false); echo "status: $?"; }
check_goodstatus: 1Top level లోని సాధారణ assignment తన చివరి command substitution స్థితిని ఇప్పటికే నివేదిస్తుంది:
out=$(exit 3)
echo $?3systemd service మరియు timer కింద నడిచే health check లో ఇది ముఖ్యంగా ముఖ్యం. Exit code దాచబడితే, తనిఖీ చేయాల్సిన పని అసలు జరగకపోయినా unit ప్రతి run లో విజయాన్ని నివేదిస్తుంది.
మీకు నిజంగా అవసరమైన in-shell ప్రత్యామ్నాయాలు
చాలామంది variable లో data కావాలనే ఉద్దేశంతో $(...) ను ఉపయోగిస్తారు. కానీ చాలాసార్లు వారికి నిజంగా కావలసింది capture కాదు, input. ఈ నాలుగు రూపాలు state ను ప్రస్తుత shell లోనే ఉంచుతాయి.
loop పై redirect
cd /tmp/subst-demo
while read -r line; do printf 'got: %s\n' "$line"; done < three.txtgot: alpha
got: beta
got: gammaInput కోసం ఎలాంటి process సృష్టించబడదు. అందువల్ల loop body లో సెట్ చేసినవి loop పూర్తైన తర్వాత కూడా అందుబాటులో ఉంటాయి.
Process substitution
while read -r line; do printf 'got: %s\n' "$line"; done < <(sort -r three.txt)got: gamma
got: beta
got: alpha<(command) మీకు ఒక path ను అందిస్తుంది. అది /dev/fd/63 లాంటిది, మరియు command output ను చదువుతుంది. అయితే command తన స్వంత process లోనే నడుస్తుంది. while loop అలా కాదు. ఇదే దీని ముఖ్య ఉద్దేశం. < <( లోని space తప్పనిసరి. <<( ను here-document ప్రారంభంగా చదువుతుంది కాబట్టి అది parse కాదు. Process substitution అనేది bash feature. అందువల్ల Ubuntu లేదా Debian లో #!/bin/sh ఉన్న script dash కింద నడుస్తుంది, కానీ దానిపై విఫలమవుతుంది. #!/bin/bash ను ఉపయోగించండి.
Here-strings
read -r first rest <<< 'alpha beta gamma'
echo "$first"
echo "$rest"alpha
beta gamma<<< ఒక string ను command యొక్క standard input కు అందిస్తుంది. read మీ shell లోనే నడుస్తుంది. అందువల్ల రెండు variables ను మీరు ఉపయోగించే స్థలంలోనే set చేయవచ్చు. ఒక output line నుంచి రెండు fields తీసుకోవడానికి read -r a b <<< "$(some-command)" సాధారణ పద్ధతి.
పూర్తి files కోసం mapfile
mapfile -t lines < three.txt
echo "${#lines[@]}"
echo "${lines[1]}"3
betamapfile, దీనిని readarray అని కూడా రాస్తారు, ప్రస్తుత shell లో file ను array గా చదువుతుంది. -t ప్రతి element చివరలోని newline ను తొలగిస్తుంది. దీనికి bash 4 లేదా తదుపరి version అవసరం. Ubuntu 24.04 తో bash 5.2 విడుదలవుతుంది. కాబట్టి ఇది ప్రస్తుత server image ఏదైనా అందుబాటులో ఉంటుంది.
మీరు script ను commit చేయడానికి ముందు checklist
$(command)రాయండి. మీరు ప్రత్యేకంగా splitting కోరుకుంటే తప్ప, దాన్ని"$(command)"గా quote చేయండి.- చివరలోని newlines తొలగిపోయాయని భావించండి. అవి మళ్లీ అవసరమైతే sentinel character జోడించండి.
- substitutions లో interactive commands ఉపయోగించవద్దు. లేదా వాటి prompts ను standard error కు పంపండి.
out=$(command)యొక్క exit status ముఖ్యమైనప్పుడు,local outను ప్రత్యేక line లో రాయండి.- input నుంచి variables సెట్ చేయడానికి pipe బదులుగా redirect లేదా process substitution ఉపయోగించండి.
కొత్త VPS ను సెటప్ చేస్తున్నప్పుడు వ్యక్తులు మొదట రాసే చిన్న scripts లో ఈ నిర్మాణాలు తరచుగా కనిపిస్తాయి. వీటిలోని failures నిశ్శబ్దంగా ఉంటాయి. ఒక prompt ను filename లోకి capture చేసిన backup script అయినా, exit code ను mask చేసిన health check అయినా success ను report చేస్తూనే ఉంటుంది. మీరు ఒకే script ను అనేక servers పై run చేసినప్పుడు సమస్య మరింత పెరుగుతుంది. మీరు చదవని output ఇప్పుడు ఇరవై machines పై కూడా మీరు చదవని output అవుతుంది.
FAQ
$() లోని cd ప్రస్తుత directory ని ఎందుకు మార్చదు?
$(...) తన command ను subshell లో నడుపుతుంది. ఇది మీ working directory మరియు variables యొక్క ప్రతిని కలిగి ఉన్న వేరు process. cd ఆ ప్రతిని మార్చుతుంది. తరువాత process ముగుస్తుంది, ఆ ప్రతీ తొలగించబడుతుంది. Subshell తన standard output మరియు exit status మాత్రమే తిరిగి ఇవ్వగలదు. అందువల్ల directory మార్పును parent shell కు పంపే విధానం ఉండదు. Directory path మాత్రమే కావాలంటే, దాన్ని target=$(cd /etc && pwd) తో capture చేసి "$target" ను ఉపయోగించండి. Shell directory మార్చాలంటే, substitution లేకుండా cd ను నేరుగా అమలు చేయండి.
bash లో $() మరియు backticks మధ్య తేడా ఏమిటి?
సాధారణ commands కు ఇవి ఒకే ఫలితాన్ని ఇస్తాయి. అయితే ముఖ్యమైన రెండు తేడాలు ఉన్నాయి. $() నేరుగా nested అవుతుంది, ఎందుకంటే parser parentheses ను జత చేస్తుంది. Backticks లో ప్రతి nesting స్థాయికి escaped backtick అవసరం. Inner command ను parse చేయడానికి ముందు backticks ఒక స్థాయి backslash escaping ను కూడా తొలగిస్తాయి. అందువల్ల ` echo 'a\\b' prints a\b while $(echo 'a\\b') prints a\\b. $() is in POSIX and works in dash and busybox sh` వంటి సందర్భాల్లో backticks కు portability పరంగా ఎలాంటి ప్రయోజనం లేదు.
Command ఒక ప్రశ్న అడిగినప్పుడు prompt లేకుండా నా script ఎందుకు hang అవుతుంది?
Command substitution standard output ను pipe లోకి redirect చేస్తుంది. కానీ standard input మీ terminal కు అనుసంధానంగానే ఉంటుంది. Prompt ను standard output కు రాసే program లో, ఆ prompt variable లో capture అవుతుంది. అయితే దాని వెనుక ఉన్న read మీ సమాధానం కోసం వేచి ఉంటుంది. Terminal మీరు type చేసే characters ను మాత్రమే terminal driver echo చేయడంతో చూపిస్తుంది. ప్రశ్నను substitution వెలుపలికి మార్చండి. లేదా prompt capture కాకుండా ఉండేందుకు printf 'Username: ' >&2 తో దాన్ని standard error కు రాయండి.
నా variable చివర్లోని ఖాళీ lines ఎందుకు తొలగిపోయాయి?
Command substitution చివరలోని newline లన్నింటినీ తొలగిస్తుంది. చివరి newline ఒక్కటినే కాదు. printf 'hello\n\n\n' > f; v=$(cat f) వలన v లో ఐదు bytes మాత్రమే ఉంటాయి, అయితే file లో ఎనిమిది bytes ఉంటాయి. వాటిని ఉంచాలంటే substitution లోపల sentinel ను append చేయండి. తరువాత v=$(cat f; printf x) ను ఉపయోగించి, దాని తర్వాత v=${v%x} తో sentinel ను తొలగించండి. Sentinel newlines తర్వాత ఉంటుంది. అందువల్ల bash తొలగించడానికి చివరలో ఏమీ ఉండదు.
local out=$(cmd) ఎల్లప్పుడూ success ను ఎందుకు report చేస్తుంది?
local స్వతంత్ర command. ఆ line తర్వాత $?, variable declaration లో local విజయవంతమైందో లేదో report చేస్తుంది. Substitution యొక్క exit status ఉపయోగించబడకుండా discard అవుతుంది. అందువల్ల set -e కూడా script ను ఆపదు. declare, export, typeset మరియు readonly కూడా ఇదే విధంగా పనిచేస్తాయి. local out ను ఒక line లో, out=$(cmd) ను తదుపరి line లో రాయండి. అప్పుడు $? నిజమైన status ను report చేస్తుంది.