如何使脚本显示并将其自己的输出保存到唯一的文件名?



我有一个基本的 I/O 脚本,我将调用test.sh,其中包含基于用户输入的一堆各种输出。 基本示例如下:

echo "Enter username"
read USERNAME
grep $USERNAME /etc/passwd
grep $USERNAME /etc/group
echo "Done"
exit 0

我需要这个脚本在运行时显示和保存所有输出。 我了解如何通过管道传输带有 tee 的单个命令以显示并保存到sh test.sh | tee new.txt等文件中,但我迷失了如何让脚本本身在运行时显示它时保存其整个输出。

我还想使脚本在每次运行时都另存为唯一的文件名,文件名USERNAME-DATETIME.rpt。 我尝试过四处研究,但我能找到的只是如何使用重定向和管道运行脚本,而不是如何在运行时保存脚本本身。

如何显示脚本输出并将其保存到唯一的文件名?

您可以使用exec将脚本中的所有后续输出(重定向到 stdout):

exec 1>"/path/to/$USERNAME-$(date -Is).rpt"

(获取USERNAME后插入它。

如果您希望同时重定向 stderr 和 stdout,请使用&快捷方式(仅限 bash):

exec &>"/path/to/$USERNAME-$(date -Is).rpt"

编辑:请注意,exec 1>file会将所有输出重定向到一个文件。要在终端上显示它(复制它),您可以将重定向exec进程替换结合起来:

exec &> >(tee "/path/to/$USERNAME-$(date -Is).rpt")

您可以简单地用括号包装代码并将其重定向到您生成的唯一文件名中。(这在所有实际目的上都是独一无二的!

#!/bin/bash
myuniquefilename="$( cut -c1-8 <( date "+%S%N" | md5sum ))"
echo "Enter username"
read USERNAME
{
echo "Executing. ..."
date 
grep $USERNAME ./etc/passwd
grep $USERNAME ./etc/group
} > ${myuniquefilename}.out
echo "Done"
exit 0

您应该考虑脚本命令:

名字

script - make typescript of terminal session

概要

script [options] [file]

描述

script makes a typescript of everything displayed on your terminal.
It is useful for students who need a hardcopy record of an
interactive session as proof of an assignment, as the typescript file
can be printed out later with lpr(1).
If the argument file is given, script saves the dialogue in this
file.  If no filename is given, the dialogue is saved in the file
typescript.
  1. 只需键入script myfile.txt即可。

  2. 执行您想要的任何 shell 命令。

  3. 完成后输入<Ctl-D>

瞧! 您的会话将写入"myfile.txt"。

作为适用于所有POSIX 兼容 shell 的 @randomir 答案的替代方法:

# note that using "fifo.$$" is vulnerable to symlink attacks; execute only in a directory
# where you trust no malicious users have write, or use non-POSIX "mktemp -d" to create a
# directory to put the FIFO in.
fifo_name="fifo.$$"
mkfifo "fifo.$$"
tee "/path/to/$USERNAME-$(date "+%Y-%m-%dT%H:%M:%S").rpt" >"fifo.$$" &
exec >"fifo.$$" 2>&1 # redirects both stdout and stderr; remove the "2>&1" for only stdout

请务必在脚本完成执行时rm -f "fifo.$$"


在整个脚本的上下文中,使用其他一些最佳实践修复,这可能如下所示:

#!/bin/sh
echo "Enter username: " >&2
read -r username
fifo_name="fifo.$$"
mkfifo "fifo.$$"
tee "/path/to/$username-$(date "+%Y-%m-%dT%H:%M:%S").rpt" >"fifo.$$" &
exec >"fifo.$$" # 2>&1 # uncomment the 2>&1 to also redirect stderr
# look only for exact matches in the correct field, not regexes or substrings
awk -F: -v username="$username" '$1 == username { print $0 }' /etc/passwd
awk -F: -v username="$username" '$1 == username { print $0 }' /etc/group
echo "Done" >&2 # human-targeted status messages belong on stderr, not stdout
rm -f "$fifo_name"

注意:

  • date -Is是GNUism,在BSD日期中不可用,当然也不能保证在POSIX平台上。因此,如果您不知道 shell 将在哪个平台上运行,传递显式格式字符串是安全的事情。
  • grep查找正
  • 则表达式作为子字符串 - 当它被告知查找jdoug时,它可以找到用户jdouglas,或者在查找foo.bar时用户foo1bar(因为点是正则表达式中的通配符)。它还可以与不同字段中的信息进行匹配(带有人名的GECOS字段,主目录等)。告诉awk在可以找到它的确切字段中查找确切的字符串要安全得多。

最新更新