读取 shell 脚本中的行输出

Read line output in a shell script

我想从 shell 脚本中 运行 一个程序(执行时会产生日志数据)并将输出写入文本文件。我没有这样做:/

$prog 是执行的程序 -> socat /dev/ttyUSB0,b9600 STDOUT

$log/$FILE 只是 .txt 文件的路径

我有一个 Perl 脚本来执行此操作:

open (S,$prog) ||die "Cannot open $prog ($!)\n";
open (R,">>","$log") ||die "Cannot open logfile $log!\n";

while (<S>) {
    my $date = localtime->strftime('%d.%m.%Y;%H:%M:%S;');
    print "$date$_";
}

我试图在这样的 shell 脚本中做到这一点

#!/bin/sh

FILE=/var/log/mylogfile.log

SOCAT=/usr/bin/socat
DEV=/dev/ttyUSB0
BAUD=,b9600
PROG=$SOCAT $DEV$BAUD STDOUT

exec 3<&0
exec 0<$PROG
while read -r line
do
        DATE=`date +%d.%m.%Y;%H:%M:%S;`
        echo $DATE$line >> $FILE
done
exec 0<&3

根本不起作用...

如何读取该程序的输出并使用 shell 脚本将其通过管道传输到我的文本文件中?我做错了什么(如果我没有做错的话)?


最终代码:

#!/bin/sh

FILE=/var/log/mylogfile.log

SOCAT=/usr/bin/socat
DEV=/dev/ttyUSB0
BAUD=,b9600
CMD="$SOCAT $DEV$BAUD STDOUT"

$CMD |
while read -r line
do
    echo "$(date +'%d.%m.%Y;%H:%M:%S;')$line" >> $FILE
done

原回答

您没有显示错误消息 — 这会有所帮助。

不过,您的问题可能是这一行:

DATE=`date +%d.%m.%Y;%H:%M:%S;`

其中分号标记命令的结束,并且可能没有命令 %H 可以执行任何有用的操作等

您需要在 date 的格式参数周围加上引号,而我将在这项工作中使用单引号:

DATE=$(date +'%d.%m.%Y;%H:%M:%S;')

甚至将循环体中的两行替换为:

echo "$(date +'%d.%m.%Y;%H:%M:%S;')$line" >> $FILE

双引号可以防止各种问题。

假设您解决了一系列其他问题,例如变量 FILEprog 的设置。另外,我可能会使用:

exec > $FILE

初始 zap 输出文件,然后所有后续标准输出将转到该文件,因此 echo 行变为:

echo "$(date +'%d.%m.%Y;%H:%M:%S;')$line"

修改后的答案

这道题原本漏掉了很多关键信息。它最终得到了更新,包含了完整的代码。

我最初确定的问题仍然是一个问题,但您没有运行解决它,因为输入重定向不起作用。如果您希望输入来自进程,请使用管道,或者可能 process substitution。但是,请注意,您将 #!/bin/sh 作为您的 shebang 行,并且 /bin/sh 不会识别进程替换;要么改变 shebang 要么使用管道符号。请注意,如果循环正在设置需要在循环完成后访问的变量,则进程替换具有优势。

$SOCAT $DEV$BAUD STDOUT |
while read -r line
do
    …
done

while read -r line
do
    …
done < <($SOCAT $DEV$BAUD STDOUT)

请注意,您的代码包含以下行:

PROG=$SOCAT $DEV$BAUD STDOUT

此 运行 是由 $DEV$BAUD 标识的命令,参数 STDOUT 和环境变量 PROG 设置为 $SOCAT 的值。那不是你想要的。

您可以使用数组:

PROG=($SOCAT $DEV$BAUD STDOUT)

然后 运行:

"${PROG[@]}"

在管线中:

"${PROG[@]}" |
while read -r line
do
    …
done

或使用进程替换:

while read -r line
do
    …
done < <("${PROG[@]}")

请注意,除非在最后的 exec 0<&3 之后有代码,否则涉及文件描述符 3 的重定向没有什么特别的优点。您也应该在完成后关闭 3:

exec 0<&3 3>&-

'final' 代码包括以下行:

CMD="$SOCAT $DEV$BAUD STDOUT"

$CMD |
while read -r line

这可以正常工作,因为命令的参数中没有空格。这是一种常见情况,但要注意参数和文件路径中的空格。

要从进程中读取,请使用进程替换

exec 0< <( $PROG )

/bin/sh不支持,所以用/bin/bash代替。

要将多个单词分配给变量、引号或反斜杠空格:

PROG="$SOCAT $DEV$BAUD STDOUT"

分号在 shell 中是特殊的,引号或反斜杠:

DATE=$(date '+%d.%m.%Y;%H:%M:%S;')

此外,不需要 exec:

while ...
    ...
done < <( $PROG )

您甚至可以在 done 之后添加 > $FILE 而不是将每一行单独添加到文件中。