Bash, stdin 파일 이름?

Bash, stdin 파일 이름?

stdin에서 "<" 및 ">"와 같은 기호를 계산하는 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

이 방법이 좋은가요? stdin에서 파일 이름 "example.html"을 얻는 방법을 모르겠습니다.

답변1

요점은 stdin무엇이든 될 수 있다는 것입니다. 예를 들어 파이프, 네트워크 소켓, 일반 파일, 장치가 될 수 있으며 스크립트가 시작될 때 일반 파일의 절반이 될 수 있습니다... 가능하다면 한 번에 데이터를 처리하지 않으면 다음으로 제한됩니다.탐색 가능한즉, 일반 파일과 몇 가지 장치 파일이거나 정보를 어떻게든(임시 파일이나 메모리에...) 저장해야 합니다. 하지만 여기에서는 모든 정보를 한 번에 얻을 수 있습니다.

예를 들어 다음을 수행할 수 있습니다.

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

POSIX적으로:

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'

(그러나 $$쉘을 실행한 프로세스의 pid이기 때문에 표준 입력이 리디렉션된 서브쉘에서는 작동하지 않습니다)

이제 읽기 위해 반드시 해당 파일을 다시 열 수 있는 것은 아니며, 그렇게 한다고 해도 해당 파일을 읽어도 동일한 데이터가 다시 제공되지 않을 수 있습니다(예를 들어 파이프를 생각해 보세요).

$ 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/stdinstdin이 일반 파일인 경우 여는 것은 처음부터 파일을 다시 읽는 반면, 다른 시스템에서 /dev/stdin을 여는 것은 a와 유사합니다. 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

stdin에서 파일 내용을 읽는 것이 요구 사항이 아닌 경우 파일 이름을 스크립트에 인수로 전달할 수 있습니다.

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

관련 정보