如何处理从UNIX shell脚本中的标准输入读取的行?

时间:2013-10-31 05:11:00

标签: shell unix

我被这个问题困住了: 我编写了一个shell脚本,它从stdin获取了一个包含许多行的大文件,这就是它的执行方式:

./script < filename

我想使用该文件作为脚本中另一个操作的输入,但我不知道如何将该文件的名称存储在变量中。
它是一个脚本,它从stdin获取一个文件作为参数,然后在它自己的文件中执行awk操作。如果我用脚本写的话说:

script:
#!/bin/sh
...
read file
...
awk '...' < "$file"
...

它只读取输入文件的第一行。 我找到了这样写的方法:

Min=-1
while read line; do
    n=$(echo $line | awk -F$delim '{print NF}')   
    if [ $Min -eq -1 ] || [ $n -lt $Min ];then
    Min=$n
    fi
done

等待处理需要很长时间,似乎awk需要很长时间。 那么如何改进呢?

5 个答案:

答案 0 :(得分:2)

你过度了。您调用脚本的方式:

  • 文件内容是脚本的标准输入
  • 脚本不接受任何参数

但是awk默认情况下已经从stdin获取输入,所以你需要做的就是:

  • 不给awk任何文件名参数,它将自动成为包装shell的标准输入
  • 在包装脚本到达awk部分之前不消耗任何输入。具体来说:没有read

如果您的脚本完全存在,那么它会缩减到awk调用,因此您可以考虑完全取消它,直接调用awk。或者直接将您的脚本设为awk而不是sh

除此之外:您的while read line /多个awk变体(问题中的变体)速度慢的原因是因为它为输入的每一行产生awk进程,和处理产生的处理速度比处理单行的awk慢。生成tmpfile / single awk变体(你的答案中的变体)仍然有点慢的原因是因为它逐行生成tmpfile,每次都重新打开以附加。

答案 1 :(得分:2)

/dev/stdin在这里非常有用。 事实上,它只是一系列指向您输入的链接。

因此,编写cat /dev/stdin将为您提供文件中的所有输入,您可以拒绝使用输入文件名。

现在回答问题:)递归阅读链接,从/dev/stdin开始,你会得到文件名。 Bash代码:

r(){
    l=`readlink $1`
    if [ $? -ne 0 ]
    then
        echo $1
    else
        r $l
    fi
}
filename=`r /dev/stdin`
echo $filename

UPD: 在Ubuntu中,我找到了一个选项-f来读取链接。即readlink -f /dev/stdin给出相同的输出。某些系统可能缺少此选项。

UPD2:测试(test.sh是上面的代码):

$ ./test.sh <input # that is a file
/home/sfedorov/input
$ ./test.sh <<EOF
> line
> EOF
/tmp/sh-thd-214216298213
$ echo 1 | ./test.sh 
pipe:[91219]
$ readlink -f /dev/stdin < input 
/home/sfedorov/input
$ readlink -f /dev/stdin << EOF
> line
> EOF
/tmp/sh-thd-3423766239895 (deleted)
$ echo 1 | readlink -f /dev/stdin
/proc/18489/fd/pipe:[92382]

答案 2 :(得分:0)

将脚本修改为将输入文件名作为参数,然后从脚本中的文件中读取:

$ ./script filename

script

filename=$1
awk '...' < "$filename"

如果您的脚本只是从标准输入读取,则无法保证提供输入的命名文件;它可以很容易地从管道或网络插座中读取。

答案 3 :(得分:0)

如何以不同方式调用脚本将YourFilename的标准输出管道输入 你的scriptName如下(cat文件名的标准输出现在变成标准 输入到您的脚本,实际上在这种情况下是awk命令 因为我有文件名Names.data和脚本showNames.sh执行如下

cat Names.data | ./showNames.sh

文件名Names.data的内容 哈克贝利·芬恩 杰克斯普拉特 Humpty Dumpty

scrip的内容; t showNames.sh

#!/bin/bash
#whatever awk commands you need
awk  "{ print }"

答案 4 :(得分:-2)

好吧,我终于找到了解决问题的方法,虽然这需要几秒钟。

grep '.*' >> /tmp/tmpfile
Min=$(awk -F$delim 'NF < min || min == "" { min = NF };END {printmin}'</tmp/tmpfile)

只需将每一行附加到一个临时文件中,以便在从stdin读取后,tmpfile与输入文件相同。