#!/bin/sh
TMP=$(cd Folder && ls)
for name in $TMP; do
if [[ "${name}" != *"a"* -a ${name} == *"b"* ]] ;then
echo $name
fi
done
我试图输出其中没有“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
我在这里做错了什么?
答案1
要打印包含且不Folder
包含、 in及其(b
a
zsh
~
除了/并不是) 全局运算符:
#! /bin/zsh -
set -o extendedglob
print -rC1 -- Folder/(*b*~*a*)(N:t)
(删除N
(对于nullglob
,如果您希望在没有匹配文件的情况下报告错误)。
在(引入该运算符(不是标准语法的一部分)ksh93
的 shell及其([[...]]
sh
&
和) 和!(...)
(不是) 运算符:
#! /bin/ksh93 -
(
CDPATH= cd Folder &&
set -- ~(N)@(*b*&!(*a*)) &&
(($# > 0)) &&
printf '%s\n' "$@"
)
使用bash
shell(它zsh
也复制了 ksh 的[[...]]
运算符),使用extglob
支持 ksh88 glob 运算符并nullglob
获得 zsh 的N
glob 限定符或ksh93
的~(N)
glob 运算符以及一些双重否定|
(或者) 实现和:
#! /bin/bash -
(
shopt -s extglob nullglob
cd ./Folder &&
set -- !(!(*b*)|*a*) &&
(($# > 0)) &&
printf '%s\n' "$@"
)
在标准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' "$@"
)
请注意,如果您具有读取权限但没有搜索权限,则使用的解决方案cd
将不起作用。Folder
更一般地说,要回答如何检查字符串中是否包含另一个字符串的一般问题sh
,最好是case
使用在 中进行模式匹配的构造sh
。您甚至可以使用辅助函数:
contains()
case "$1" in
(*"$2"*) true;;
(*) false;;
esac
使用就像:
if contains foobar o; then
echo foobar contains o
fi
if ! contains foobar z; then
echo foobar does not contain o
fi
if
contains "$file" b &&
! contains "$file" a
then
printf '%s\n' "$file contains b and not a "
fi
答案2
[[ ... ]]
您的主要问题是您在执行的脚本中使用模式匹配,/bin/sh
而该脚本不支持这些类型的测试。另请参阅标题为的问题从 sh 文件运行时,Shell 脚本会引发未找到错误。但如果手动输入命令就可以工作
另一个问题是,您将 的输出组合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
这将循环遍历Folder
目录中包含该字母的所有文件b
,然后打印其中不包含a
.角色的测试a
是使用 完成的case ... esac
。该*a*
模式与文件名中的某个字符匹配a
(如果有),在这种情况下什么也不会发生。这些语句处于“默认情况”,如果字符串中printf
没有,则执行该语句。a
-e
使用和进行测试-L
是为了确保我们捕获循环模式与任何内容都不匹配的边缘情况。在这种情况下,模式将保持未展开状态,因此为了确保我们可以区分未展开的模式和实际存在的文件,我们使用-e
.该-L
测试是为了捕获与循环模式同名的符号链接的进一步边缘情况,但它不指向任何有效的地方。下面的代码bash
不需要这个,因为它使用nullglob
shell 选项(在 参考资料中不可用/bin/sh
)。
在模式扩展上运行循环*b*
而不是任何ls
输出的好处是,它还能够处理包含空格、制表符、换行符和文件名通配符的文件名(文件名永远不会放入我们随后尝试的单个字符串中)迭代)。
在bash
shell 中,您可以使用模式匹配测试执行相同的操作[[ ... ]]
,就像您自己尝试做的那样:
#!/bin/bash
cd Folder || exit 1
shopt -s nullglob
for name in *b*; do
[[ $name != *a* ]] && printf '%s\n' "$name"
done
也可以看看: