AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 693003
Accepted
shikhanshu
shikhanshu
Asked: 2022-03-05 22:16:00 +0800 CST2022-03-05 22:16:00 +0800 CST 2022-03-05 22:16:00 +0800 CST

使用多种模式运行`ls`时避免非零退出代码

  • 772

假设我有两个可能的路径,我想在 Linux 机器上列出目录和文件:

/some/path1/
/some/path2/

如果我在 中执行以下操作tcsh,我会得到0退出代码,如果至少有一个path1或path2存在:

ls -d /some/{path1,path2}/*

但是,如果我在 中执行完全相同的操作bash,我会得到2退出代码,并显示stderr消息报告path1不存在(如果 path1 是不存在的那个)。

在这种情况下,我该如何bash表现tcsh?如果至少存在一条路径,ls我可以要求它回馈吗?0如果两者都不存在,我确实期望非零代码,这就是tcsh回馈。

bash wildcards
  • 2 2 个回答
  • 991 Views

2 个回答

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2022-03-06T06:21:21+08:002022-03-06T06:21:21+08:00

    您的大部分问题已经在为什么 nullglob 不是默认值?.

    要记住的一件事是:

    ls -d /some/{path1,path2}/*
    

    在csh//// tcsh(但不是zsh,见下文bash)中是一样的:kshfish

    ls -d /some/path1/* /some/path2/*
    

    由于大括号扩展是在glob 扩展之前执行的(不是作为./some/pathx/*ls

    bash,就像ksh它主要复制的一样,它继承了 Bourne shell 引入的错误功能,即当 glob 模式与任何文件都不匹配时,它按原样传递,字面上作为命令的参数。

    因此,在这些 shell 中,如果/some/path1/*匹配至少一个文件并且/some/path2/*不匹配任何文件,ls则将使用-d、/some/path1/file1和/some/path1/file2文字/some/path2/*作为参数调用。由于/some/path2/*文件不存在,ls会报错。

    csh其行为类似于早期的 Unix 版本,其中 glob 不是由 shell 执行,而是由/etc/glob辅助实用程序(将它们的名称命名为 glob)执行。该助手将在调用命令之前执行 glob 扩展,如果所有glob 模式都无法匹配任何文件,则在No match不运行命令的情况下报告错误。否则,只要至少有一个匹配的 glob,所有不匹配的都会被简单地删除。

    因此,在我们上面的示例中,使用csh/tcsh或古老的 Thompson shell 及其/etc/glob助手,ls将使用-d,/some/path1/file1和/some/path1/file2only 调用,并且可能会成功(只要/some/path1是可搜索的)。

    zsh既是 Korn-like 又是 csh-like shell。它没有 Bourne shell 的错误功能,即不匹配的 glob 按原样传递¹,但默认情况下比csh这更严格,所有失败的 glob 都被视为致命错误。因此zsh,在默认情况下,如果其中一个/some/path1/*或/some/path2/*(或两者)不匹配,则该命令被中止。可以使用option²在bashshell中启用类似的行为。failglob

    这使得行为更加可预测/一致,但意味着当您想要将多个 glob 扩展传递给命令并且不希望它失败时,只要其中一个 glob 成功,您就会遇到该问题。但是,您可以设置cshnullglob选项以获得类似 csh 的行为(或emulate csh)。

    这可以通过使用匿名函数在本地完成:

    () { set -o localoptions -o cshnullglob; ls -d /some/{path1,path2}/*; }
    

    或者只是使用一个子shell:

    (set -o cshnullglob; ls -d /some/{path1,path2}/*)
    

    但是,在这里,您可以使用交替 glob 运算符来使用与所有它们匹配的一个,而不是使用两个 glob:

    ls -d /some/(path1|path2)/*
    

    在这里,您甚至可以这样做:

    ls -d /some/path[12]/*
    

    在bash中,您可以启用extglobbash 选项以支持 ksh 的扩展 glob 运算符的子集,包括交替:

    (shopt -s extglob; ls -d /some/@(path1|path2)/*)
    

    现在,由于从 Bourne shell 继承的错误功能,如果该 glob 不匹配任何文件,/some/@(path1|path2)/*将按原样传递给ls并ls最终列出一个名为literal 的文件/some/@(path1|path2)/*,因此您还希望启用failglob选项谨防:

    (shopt -s extglob failglob; ls -d /some/@(path1|path2)/*)
    

    或者,您可以使用nullglob选项(从bash复制zsh)将所有不匹配的 glob 扩展为空。但:

    (shopt -s nullglob; ls -d /some/path1/* /some/path2/*)

    在命令的特殊情况下是错误的ls,如果没有传递任何参数列表.。但是,您可以使用nullglob将 glob 扩展存储到数组中,并且仅ls在数组成员为非空时将其作为参数调用:

    (
      shopt -s nullglob
      files=( /some/path1/* /some/path2/* )
      if (( ${#files[@]} > 0 )); then
        ls -d -- "${files[@]}"
      else
        echo >&2 No match
        exit 2
      fi
    )
    

    在zsh中,而不是全局启用,您可以使用glob 限定符(启发 ksh 的,尚未复制)nullglob在每个 glob 的基础上启用它,并再次使用匿名函数而不是数组:(N)~(N)bash

    () {
      (( $# > 0 )) && ls -d -- "$@"
    } /some/path1/*(N) /some/path2/*(N)
    

    shell 现在的fish行为类似于zsh失败的 glob 导致错误的地方,除了当 glob 与for, set(用于分配数组)一起使用或count它以某种nullglob方式运行时。

    此外,在 中fish,虽然大括号扩展本身不是 glob 运算符,但它是作为通配的一部分完成的,或者当大括号扩展与通配相结合并且至少可以返回一个元素时,至少一个命令不会中止。

    所以,在fish:

    ls -d /some/{path1,path2}/*
    

    最终会表现得像 in csh。

    甚至:

    {ls,-d,/xx*}
    

    如果不匹配而不是失败(在这种情况下与 csh 表现不同),将导致单独ls调用。-d/xx*

    无论如何,如果只是print匹配文件路径,则不需要ls. 在zsh中,您可以使用其print内置的列打印:

    print -rC3 /some/{path1,path2}/*(N)
    

    将在 3 列上打印路径raw(如果与Nullglob glob 限定符不匹配,则不打印任何内容)。

    相反,如果您想检查这两个目录中是否至少有一个非隐藏文件,您可以执行以下操作:

    # bash
    if (shopt -s nullglob; set -- /some/path1/* /some/path2/*; (($#))); then
       echo yes
    else
       echo no
    fi
    

    或者使用一个函数:

    has_non_hidden_files() (
      shopt -s nullglob
      set -- "$1"/*
      (($#))
    )
    if has_non_hidden_files /some/path1 || has_non_hidden_files /some/path2
    then
      echo yes
    else
      echo no
    fi
    
    # zsh
    if ()(($#)) /some/path1/*(N) /some/path2/*(N); then
      echo yes
    else
      echo no
    fi
    

    或者有一个功能:

    has_non_hidden_files() ()(($#)) $1/*(NY1)
    if has_non_hidden_files /some/path1 || has_non_hidden_files /some/path2
    then
      echo yes
    else
      echo no
    fi
    

    (Y1作为找到第一个文件后停止的优化)

    请注意has_non_hidden_files,对于用户无法读取的目录(无论它们是否有文件),它们会(默默地)返回 false。在中,您可以通过其特殊变量zsh检测到这种情况。$ERRNO


    ¹ Bourne 行为(由 POSIX 指定)可以zsh通过 doemulate sh或emulate ksh或 with启用set +o nomatch

    ² 请注意,当 glob 不匹配时,具体取消的行为存在显着差异,这种fish行为通常更明智,也bash -O failglob可能是最糟糕的

    • 23
  2. terdon
    2022-03-06T03:32:29+08:002022-03-06T03:32:29+08:00

    只需阅读Stéphane 的回答。


    发生这种情况是因为如何bash对待不匹配任何内容的 glob。要获得您在 中观察到的相同行为tcsh,您需要启用 bash 的nulglob选项。来自man bash:

    空球

    如果设置,bash 允许不匹配文件的模式(参见上面的路径名扩展)扩展为空字符串,而不是它们本身。

    启用nullglob,shopt -s nullglob您将获得预期的行为:

    $ ls
    bar
    $ ls {bar,foo}/*
    ls: cannot access 'foo/*': No such file or directory
     bar/ff
    $ echo $?
    2
    
    
    $ shopt -s nullglob 
    $ ls {bar,foo}/*
    bar/ff
    $ echo $?
    0
    
    • 4

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve