2

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

./script < filename

我想将该文件用作脚本中另一个操作的输入,但是我不知道如何将此文件的名称存储在变量中。
它是一个脚本,它从标准输入中获取一个文件作为参数,然后在这个文件中执行 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 需要很长时间。那么如何改进呢?

4

5 回答 5

2

你做的太过分了。您调用脚本的方式:

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

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

  • 不提供awk任何文件名参数,它将自动成为包装外壳的标准输入
  • 在包装脚本到达部件之前不要使用任何输入awk。具体:没有read

如果这就是您的脚本的全部内容,那么它会简化为awk调用,因此您可能会考虑完全取消它并直接调用awk。或者直接使您的脚本成为awk一个而不是sh一个。

另外:您的while read line/multipleawk变体(问题中的那个)缓慢的原因是因为它awk为输入的每一行生成一个进程,并且进程生成比awk处理单行慢几个数量级。生成 tmpfile/singleawk变体(您的答案中的那个)仍然有点慢的原因是因为它逐行生成 tmpfile,每次都重新打开以追加。

于 2013-10-31T05:19:27.580 回答
2

/dev/stdin在这里可能非常有用。实际上,它只是您输入的链接链。

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

现在回答问题:) 递归读取链接,从 开始/dev/stdin,您将获得文件名。巴什代码:

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:tests(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]
于 2013-10-31T10:56:00.197 回答
0

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

猫名字.data | ./showNames.sh

文件名 Names.data 的内容 Huckleberry Finn Jack Spratt Humpty Dumpty

脚本的内容;t showNames.sh

#!/bin/bash
#whatever awk commands you need
awk  "{ print }"
于 2013-10-31T15:52:56.400 回答
0

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

$ ./script filename

script

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

如果您的脚本只是从标准输入中读取,则不能保证有一个命名文件提供输入;它可以很容易地从管道或网络套接字中读取。

于 2013-10-31T13:48:01.353 回答
-2

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

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

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

于 2013-10-31T22:12:07.017 回答