如何编写一个接受文件或标准输入的脚本?


57

一个人如何编写一个接受来自文件名参数或stdin输入的脚本?

例如,您可以使用less这种方式。一个人可以less filename等效地执行cat filename | less

是否有一种简单的“开箱即用”方法?还是我需要重新发明轮子并在脚本中写一些逻辑?


@PlasmaPower只要问题是关于话题上SU,没有规定要求在不同的SE网站。许多SE网站重叠。通常,除非问题不在主题(在这种情况下,请投票决定迁移)或主题在问题上但没有得到太多回应(在这种情况下,提问者应将主持人标记为-注意/迁移,而不是交叉发布)。
鲍勃

Answers:


59

如果file参数是脚本的第一个参数,请测试是否有一个参数($1)并且它是一个文件。否则从stdin读取输入-

因此,您的脚本可能包含以下内容:

#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input

例如,那么您可以像这样调用脚本

./myscript.sh filename

要么

who | ./myscript.sh

编辑 脚本的一些说明:

[ $# -ge 1 -a -f "$1" ]-如果至少一个命令行参数($# -ge 1)和(-a运算符)的第一个参数是文件(-f测试“ $ 1”是否是文件),则测试结果为true。

&&是外壳程序逻辑AND运算符。如果test为true,则分配input="$1"cat $input输出文件。

||是Shell逻辑OR运算符。如果测试为假,则||分析以下命令。输入分配给“-”。该命令cat -从键盘读取。

总结一下,如果提供了script参数并且它是一个文件,则将变量输入分配给该文件名。如果没有有效的参数,则cat从键盘读取。


怎么 办?&& input="$1" || input="-" 为什么在test运营商之外?
cmo

我添加了一些说明的编辑,希望对您有所帮助。
嫌犯

如果脚本具有多个参数($@)怎么办?
g33kz0r

12

read从标准输入读取。从文件(./script <someinput)或通过管道(dosomething | ./script)重定向它不会使其工作有所不同。

您所需要做的就是遍历输入中的所有行(与遍历文件中的行没有什么不同)。

(示例代码,仅处理一行)

#!/bin/bash

read var
echo $var

将回显您的标准输入的第一行(通过<|)。


谢谢!我选择其他答案,因为它更适合我。我正在包装另一个脚本,在所有输入都收到之前,我不想循环(可能是很多输入...会很浪费)。
gilad hoch 2014年

4

您没有提到打算使用哪种shell,所以我假设bash是可笑的,尽管这些是跨shell的相当标准的东西。

文件参数

可以通过变量$1- $n$0返回用于运行程序的命令)访问参数。假设我有一个脚本,它只cat输出n个文件,并且它们之间有一个定界符:

#!/usr/bin/env bash
#
# Parameters:
#    1:   string delimiter between arguments 2-n
#    2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
   cat $arg
   echo $1
done

在这种情况下,我们将文件名传递给cat。但是,如果要转换文件中的数据(而无需显式写入和重写),则也可以将文件内容存储在变量中:

file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename

从stdin读取

就读取stdin而言,大多数shell都read内置了相当标准的内容,尽管指定提示的方式有所不同(至少)。

猛砸内建手册页有一个非常简明的解释read,但我更喜欢猛砸黑客网页。

只是:

read var_name

多个变量

要设置多个变量,只需将多个参数名称提供给read

read var1 var2 var3

read 然后将来自stdin的一个单词放入每个变量,将所有剩余的单词转储到最后一个变量。

λ read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
λ echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5

如果输入的单词少于变量,则剩余变量将为空(即使先前已设置):

λ read var1 var2 var3
thing1 thing2
λ echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line

提示

-p经常使用flag作为提示:

read -p "Enter filename: " filename

注意:ZSH和KSH(可能还有其他)对提示使用不同的语法:

read "filename?Enter filename: " # Everything following the '?' is the prompt

默认值

这并不是真正的read技巧,但我经常将它与结合使用read。例如:

read -p "Y/[N]: " reply
reply=${reply:-N}

基本上,如果变量(答复)存在,则返回自身,但如果为空,则返回以下参数(“ N”)。


4

最简单的方法是自己重定向stdin:

if [ "$1" ] ; then exec < "$1" ; fi

或者,如果您更喜欢简洁的形式:

test "$1" && exec < "$1"

现在,您脚本的其余部分可以从stdin中读取。当然,您可以通过更高级的选项解析来类似地进行操作,而不是将文件名的位置硬编码为"$1"


exec会尝试将参数作为命令执行,这不是我们想要的。
Suzana

@Suzana_K:像这里这样没有参数时就没有。在那种情况下,它只是替换shell本身的文件描述符,而不是子进程。
R ..

if [ "$1" ] ; then exec < "$1" ; fi在测试脚本中进行了复制,但由于命令未知,它会给出错误消息。与简洁形式相同。
Suzana

1
@Suzana_K:您使用的是什么外壳?如果是这样,那不是POSIX sh命令/ Bourne Shell的有效实现。
R ..

Linux Mint Qiana上的GNU bash 4.3.11
Suzana

3

使用(或断开连接)已经以此方式运行的其他东西,并使用 "$@"

假设我想编写一个工具,用制表符替换文本中的空格

tr这样做是最明显的方法,但是它只接受stdin,因此我们必须链接以下内容cat

$ cat entab1.sh
#!/bin/sh

cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ 

对于一个示例,其中所使用的工具已经表现出这种方式,我们可以sed改为使用以下方式重新实现:

$ cat entab2.sh
#!/bin/sh

sed -r 's/ +/\t/g' "$@"
$ 

3

您也可以这样做:

#!/usr/bin/env bash

# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"

# You can now use "$input_file" as your file to operate on
cat "$input_file"

有关Bash中更巧妙的参数替换技巧,请参阅this


1
这是太棒了!我正在使用uglifyjs < /dev/stdin,效果很好!
fregante

0

您也可以保持简单并使用此代码


使用此代码创建脚本文件pass_it_on.sh时,

#!/bin/bash

cat

你可以跑

cat SOMEFILE.txt | ./pass_it_on.sh

并且stdin的所有内容都将被简单地弹出到屏幕上。


或者使用此代码将stdin的副本保存在文件中,然后将其喷出到屏幕上。

#!/bin/bash

tmpFile=`mktemp`
cat > $tmpFile
cat $tmpFile    

这是另一个示例,也许更易读,在这里进行说明:

http://mockingeye.com/blog/2013/01/22/reading-everything-stdin-in-a-bash-script/

#!/bin/bash

VALUE=$(cat)

echo "$VALUE"

玩得开心。

RaamEE


0

符合POSIX的最简单方法是:

file=${1--}

等同于${1:--}

然后照常读取文件:

while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.