Shell:检查字符串是否包含指定字符

Gas*_*ton -1 shell

#!/bin/sh

TMP=$(cd Folder && ls)

for name in $TMP; do

  if [[ "${name}" != *"a"* -a ${name} == *"b"* ]] ;then
   echo $name
  fi

done
Run Code Online (Sandbox Code Playgroud)

我试图输出没有 'a' 而是 'b' 的名称。这是我得到的错误:

./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
Run Code Online (Sandbox Code Playgroud)

我在这里做错了什么?

Sté*_*las 6

要打印Folder包含b和不包含的文件名a,在zsh和它的~除了/-not) glob 运算符:

#! /bin/zsh -
set -o extendedglob
print -rC1 -- Folder/(*b*~*a*)(N:t)
Run Code Online (Sandbox Code Playgroud)

(删除N(for nullglob,如果您希望在没有匹配文件的情况下报告错误)。

ksh93(引入该[[...]]运算符(不是标准sh语法的一部分)及其&( and ) 和!(...)( not ) 运算符的外壳程序中:

#! /bin/ksh93 -
(
  CDPATH= cd Folder &&
    set -- ~(N)@(*b*&!(*a*)) &&
    (($# > 0)) &&
    printf '%s\n' "$@"
)
Run Code Online (Sandbox Code Playgroud)

bash外壳(这喜欢zsh也复制KSH的[[...]]运营商),使用extglob支持ksh88水珠运营商和nullglob获得的zsh的效果N水珠预选赛或ksh93~(N)水珠运营商和一些双重否定|)来实现

#! /bin/bash -
(
  shopt -s extglob nullglob
  cd ./Folder &&
    set -- !(!(*b*)|*a*) &&
    (($# > 0)) &&
    printf '%s\n' "$@"
)
Run Code Online (Sandbox Code Playgroud)

在标准sh语法中:

#! /bin/sh -
(
  CDPATH= cd Folder || exit
  set -- [*]b[*] *b*
  [ "$1/$2" = '[*]b[*]/*b*' ] && exit # detect the case where the pattern
                                      # expands to itself because there's
                                      # no match as there's no nullglob
                                      # equivalent in sh
  shift
  for i do
    case $i in
      (*a*) ;;
      (*) set -- "$@" "$i"
    esac
    shift
  done
  [ "$#" -gt 0 ] && printf '%s\n' "$@"
)
Run Code Online (Sandbox Code Playgroud)

请注意,cd如果您具有读取权限Folder但没有搜索权限,则使用的解决方案将不起作用。

更一般地说,要回答如何检查一个字符串是否包含另一个 in 的一般问题sh,最好使用case构造,它是您如何在sh. 您甚至可以使用辅助函数:

contains()
  case "$1" in
    (*"$2"*) true;;
    (*) false;;
  esac
Run Code Online (Sandbox Code Playgroud)

好像使用:

if contains foobar o; then
  echo foobar contains o
fi
Run Code Online (Sandbox Code Playgroud)
if ! contains foobar z; then
  echo foobar does not contain o
fi
Run Code Online (Sandbox Code Playgroud)
if
  contains "$file" b &&
    ! contains "$file" a
then
  printf '%s\n' "$file contains b and not a "
fi
Run Code Online (Sandbox Code Playgroud)


Kus*_*nda 5

您的主要问题是您在不支持这些类型的测试[[ ... ]]的脚本中使用了模式匹配/bin/sh。另请参阅题为“ Shell script throws an not found error when run from a sh file”的问题。但是如果手动输入命令工作

另一个问题是您将 的输出组合ls成单个字符串,然后将其拆分为空格、制表符和换行符上的单词,并将文件名通配符应用于生成的单词。然后你循环这个结果。

反而:

#!/bin/sh

cd Folder || exit 1

for name in *b*; do
    [ -e "$name" ] || [ -L "$name" ] || continue
    case $name in (*a*) ;; (*) printf '%s\n' "$name"; esac
done
Run Code Online (Sandbox Code Playgroud)

这将遍历Folder目录中包含字母的所有文件b,然后打印其中不包含a. a字符的测试是使用case ... esac. 该*a*模式的匹配a文件名中的字符,如果有一个,在这种情况下,没有任何反应。这些printf语句在“默认情况”中,如果a字符串中没有,则执行该语句。

使用-e-L进行测试以确保我们捕获循环模式不匹配任何内容的边缘情况。在这种情况下,模式将保持未扩展状态,因此为了确保我们可以区分未扩展模式和实际存在的文件,我们使用-e. 该-L测试是捕捉具有相同的名称作为环形图案符号链接的另一边缘的情况下,但没有指向任何地方有效。bash下面的代码不需要这个,因为它使用nullglobshell 选项(在 中不可用/bin/sh)。

*b*模式的扩展上运行循环而不是任何ls输出的好处是,这也能够处理包含空格、制表符、换行符和文件名通配符的文件名(文件名永远不会放入我们然后尝试的单个字符串中)迭代)。

bashshell 中,您可以使用[[ ... ]]模式匹配测试来做同样的事情,就像您自己尝试做的那样:

#!/bin/bash

cd Folder || exit 1

shopt -s nullglob

for name in *b*; do
    [[ $name != *a* ]] && printf '%s\n' "$name"
done
Run Code Online (Sandbox Code Playgroud)

也可以看看:


归档时间:

查看次数:

391 次

最近记录:

4 年,11 月 前