如果成功,则通过管道传递命令的输出


14
INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml | head -1 | xargs basename`

我只想head -1在第一个命令成功的情况下执行第二个命令()。如何改善此命令?


你说成功意味着什么?ls不会失败。
Matteo

2
但是,如果没有匹配的文件,则glob可能会失败。
Tripleee

Answers:


8

尝试这个:

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml | head -1 | xargs -r basename`

通过xargs-r标志将导致它只能运行basename如果读取的至少一个项目从标准输入(head -1)。

head -1 将运行,但您不会看到或捕获任何输出。

另外,如果您不希望用户看到任何错误输出ls,则可以将lsstderr流重定向到/dev/null

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename`

另请注意,我在左右加了引号$MY_DIR。这样,如果$MY_DIR包含空格,该命令将不会失败。

如果您正在使用bash之类的现代外壳,则应使用$( )捕获外壳而不是反引号。您还应该考虑更改变量的样式。通常应避免在脚本中使用全大写的变量名。该样式通常保留用于保留变量和环境变量。

input_file=$(ls -rt "$my_dir"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename)

如果没有文件,这不是b0rk吗?
梅尔·博伊斯

实际上:ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basename有效。
梅尔·博伊斯

@MelBoyce:我已将其添加到我的答案中。谢谢。

ls是用于交互式查看文件信息的工具。它的输出是为人类格式化的,将导致脚本中的错误。使用glob或find代替。了解原因:mywiki.wooledge.org/ParsingLs
cinelli

@cinelli:是的。我只是在回答这个问题。

9

在管道中,所有命令都同时启动和运行,而不是一个接一个地运行。因此,您需要将输出存储在某个地方。

if ls_output=$(ls -rtd -- "$MY_DIR"/FILE.*.xml); then
  first_file=$(printf '%s\n' "$ls_output" | head -n 1)
  first_file_name=$(basename -- "$first_file")
fi

请注意,它假定文件名不包含换行符。使用xargs还意味着空白字符,单引号和双引号以及反斜杠的问题。不加引号的变量将意味着空格,制表符和通配符的问题。忘记--将导致文件名以开头的问题-

要获得最早的文件的基本名称而zsh没有任何字符限制(也避免了命令参数大小有限的问题):

 first_file_name=($MY_DIR/FILE.*.xml(Om[1]:t))

如果不匹配,该命令将失败并中止脚本。您也可以这样做:

 first_file_name=($MY_DIR/FILE.*.xml(NOm[1]:t))

在这种情况下,first_file_name如果存在匹配项,则数组将包含1个元素;否则,将包含0个元素。然后,您可以执行以下操作:

 if (($#first_file_name)); then
    printf 'Match: %s\n' $first_file_name
 else
    echo >&2 No match
 fi

4

在目录中找到最新的修改文件:

latest() {
  local file path=${1:-.} ext=${2-} latest
  for file in "${path%/}"/*"$ext"; do 
    [[ $file -nt $latest ]] && latest=$file
  done
  [[ $latest ]] && printf '%s\n' "$latest"
}

用法: latest [directory/path/ [.extension]]

与其调用,不如basename使用参数扩展。

in_file=$(latest in/my/dir .xml)
base_fn=${in_file##*/} base_fn=${base_fn%.*}

在包含这些内容的目录中:

foo.xml bar.xml baz.xml newest.xml

base_fn变量的内容为: newest

为了正确使用此功能来满足您的请求目的:

check_dir=/path/to/check
check_ext=.xml
if in_file=$(latest "$check_dir" "$check_ext"); then
  base_fn=${in_file##*/} base_fn=${base_fn%.*}
else
  printf '%s\n' "No file found in $check_dir" >&2
fi

编辑:对问题的审查后,我意识到问题中的ls命令正在目录中寻找最旧的文件。可以将相同的功能重命名为oldest[[ $file -ot $oldest ]] && oldest=$file以达到相同的效果。如有任何混淆,我们深表歉意。

要注意的重要一点是,绝对不应该在人类任何情况下解析ls的输出。决不。


请注意,与ls基于方法的方法相反,如果存在符号链接,则将考虑符号链接目标的修改时间(添加-L选项以ls在此处获得相同的行为)。
斯特凡Chazelas

0

我认为最好的选择是:

ls -rf $MY_DIR/FILE.*.xml
if [ $? -eq 0 ]; then
      INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml|head -1|xargs basename `
else
      echo "Error!"
fi

如果没有文件,则ls的返回码为2,但是如果发现某个文件,则返回值为0。


2
相反,比较$?反对0,你可以这样做:if ls -rf $MY_DIR/FILE.*.xml ; then

另外,您可以将第一个命令的输出重定向到/dev/nullls -rf $MY_DIR/FILE.*.xml &> /dev/null。这样,用户将看不到额外的输出。
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.