Bash,標準輸入檔的名稱?

Bash,標準輸入檔的名稱?

我必須編寫一個 bash 腳本來計算來自標準輸入的“<”和“>”等符號。

例如:

$ ./myscript.sh <example.html
> - 20
< - 21
Found mismatching brackets!

我這樣做了:

x=`grep -o '>' example.html | wc -l`
y=`grep -o '<' example.html | wc -l`
if [ "$x" -ne "$y" ]; then
  echo "Mismatch!"
fi
echo $x
echo $y

這是好方法嗎?我不知道如何從標準輸入獲取檔案名稱“example.html”。

答案1

重點是stdin,它可以是任何東西,例如,它可以是管道、網路套接字、常規文件、設備,當腳本啟動時,它可以是常規文件的一半......如果可以的話如果您不能一次性處理數據,那麼您就將自己限制為可尋找的文件,即常規文件和一些設備文件,或必須以某種方式儲存資訊(在臨時文件或記憶體中...)。不過,在這裡可以立即獲取所有資訊。

例如,你可以這樣做:

$ grep -o '[<>]' < a.html | sort | uniq -c
     82 <
     82 >

POSIXly:

fold -w 1 a.html | grep '[<>]' | sort | uniq -c

要檢測不匹配:

if fold -w 1 a.html | awk '{n[$0]++}
     END{exit(n["<"]!=n[">"])}'
then
  echo match
else
  echo mismatch
fi

現在,為了回答本主題中的問題,在 Linux 上,您可以使用以下命令找到 stdin 的「名稱」:

readlink -f /dev/stdin

例子:

$ readlink -f /dev/stdin < a
/home/chazelas/a
$ : | readlink -f /dev/stdin
/proc/20238/fd/pipe:[758683]

(上面的20238是 的pid readlink,所以退出後該路徑就沒有多大用處了readlink,反正也不會,這pipe:[758683]只是資訊性的,不能是打開)。

更一般地說,如果lsof可用:

lsof -ad0 -p "$$" -Fn 2> /dev/null | sed -n 'n;s/^n//p'

(不過,$$作為執行 shell 的進程的 pid,它在標準輸入重定向的子 shell 中不起作用)

現在,您不一定能夠重新開啟該檔案進行讀取,即使您這樣做,從該檔案讀取也可能不會再次為您提供相同的資料(例如,考慮管道)。

$ seq 3 > a
$ { cat; cat /dev/stdin; } < a
1
2
3
1
2
3
$ cat a | { cat; cat /dev/stdin; }
1
2
3

在 Linux 上,/dev/stdin如果 stdin 是常規文件,則打開,將從頭開始再次讀取該文件,而在其他系統上,打開 /dev/stdin 更像是dup(0),即它不會將文件倒回開頭(在上面的第一個範例中) ,它將輸出1\n2\n3\n一次而不是兩次)。

答案2

您必須以某種方式儲存文件內容。您可以使用變數。

content=`cat`
x=`echo "$content" | grep -o '>' | wc -l`
y=`echo "$content" | grep -o '<' | wc -l`
if [ "$x" -ne "$y" ]; then
  echo "Mismatch!"
fi
echo $x
echo $y

或臨時檔案(如果example.html包含空位元組則必需)。

tmp=`mktemp`
trap "rm $tmp" EXIT
x=`grep -o '>' "$tmp" | wc -l`
y=`grep -o '<' "$tmp" | wc -l`
if [ "$x" -ne "$y" ]; then
  echo "Mismatch!"
fi
echo $x
echo $y

如果不需要從標準輸入讀取檔案內容,您可以將檔案名稱作為參數傳遞給腳本。

x=`grep -o '>' "$1" | wc -l`
y=`grep -o '<' "$1" | wc -l`
if [ "$x" -ne "$y" ]; then
  echo "Mismatch!"
fi
echo $x
echo $y

像這樣呼叫腳本:

$ ./myscript.sh example.html

答案3

您的任務的一種可能性是:

#!/bin/bash

if [[ -n $1 ]]; then
   if [[ ! -f $1 ]] || [[ ! -r $1 ]]; then
      echo >&2 "File \`$1' not found or not readable."
      exit 1
   fi
   exec "$0" < "$1"
fi

declare -A hary
while read c; do
   (( ++hary[$c] ))
done < <(grep -o '[<>]')

echo "> ${hary[>]}"
echo "< ${hary[<]}"

如果你呼叫這個腳本計數不匹配(您可以選擇一個較短的名稱),您將能夠使用有或沒有檔案名稱的檔案名稱。幾種可能性:

$ countmismatched example.html
$ countmismatched < example.html
$ cat example.html | countmismatched

輸出將類似:

> 41
< 42

如果需要偵測不匹配,請在腳本末尾新增:

if (( hary[<]} != hary[>] )); then
    echo "Mismatched brackets"
else
    echo "It's all good"
fi

或更明確的東西:

((difference=hary[<]-hary[>]))
if (( difference>0 )); then
    echo "Mismatched brackets: you have $difference more <'s than >'s"
elif (( difference<0 )); then
    echo "Mismatched brackets: you have $((-difference)) more >'s than <'s"
else
    echo "It's all good"
fi

相關內容