查找 exec sh:Shell 变量未传递到子 shell

查找 exec sh:Shell 变量未传递到子 shell

这是一个简化的代码,如果目录包含与父目录和扩展名相同的文件名,则打印目录的名称.md

FIND(){
    find . -type d -exec sh -c '
        for d do
            [ -f "${d}/${d##*/}.md" ] && printf "%s\n" "$d"
        done' find-sh {} +
}


FIND

概括而言,我想将搜索词${d}/${d##*/}.md作为参数发送给FIND函数,但不幸的是这不会输出任何内容:

FIND(){
    local SearchTerm="${1}"
    find . -type d -exec sh -c '
        for d do
            [ -f "${SearchTerm}" ] && printf "%s\n" "$d"
        done' find-sh {} +
}

FIND '${d}/${d##*/}.md'

我确信引用的内容存在一些问题SearchTerm。有什么提示吗?


我尝试过:FIND '\${d}/\${d##*/}.md'但没有输出

答案1

"${SearchTerm}"需要在 之外才能sh -c '进行推断,如下所示:

FIND(){
    local SearchTerm="${1}"
    find . -type d -exec sh -c '
        for d do
            [ -f "'"${SearchTerm}"'" ] && printf "%s\n" "$d"
        done' find-sh {} +
}

FIND '${d}/${d##*/}.md'

为了进一步解释一下,我们可以运行一个小测试:

SearchTerm='${d}/${d##*/}.md'
echo 'Before "$SearchTerm" After'
# output : Before "$SearchTerm" After
echo 'Before "'"$SearchTerm"'" After'
# output : Before "${d}/${d##*/}.md" After

'Before "$SearchTerm" After'是一个没有任何参数扩展的单个字符串,因为它全部用单引号引起来。

'Before "'"$SearchTerm"'" After'实际上是三个字符串连接在一起:

'Before "'# 最后,它是一个双引号,后面跟着一个单引号

"$SearchTerm"# 参数扩展发生在这里。

'" After' # 开头是单引号,后面是双引号

希望它更清楚。

答案2

您调用的内联脚本是单引号的(应该如此)。这意味着sh -cshell 将获得一个未展开的脚本"${SearchTerm}"。由于该 shell 没有SearchTerm变量,因此它的值将为空。

既然你用标签标记了你的问题,您可以传递导出函数的名称:

# Our find function.
# Takes the name of a test function that will be called
# with the pathname of a directory.
myfind () {
    local thetest="$1"

    # Run find, passing the name of the function into the in-line script.
    find . -type d -exec bash -c '
        testfunc=${1:-:}; shift
        for dirpath do
            "$testfunc" "$dirpath" && printf "%s\n" "$dirpath"
        done' bash "$thetest" {} +
}

# Our test function.
test_md_file () {
    [ -f "$1/${1##*/}.md" ]
}
export -f test_md_file

# Run the thing.
myfind test_md_file

testfunc=${1:-:}代码中的会分配$1testfunc是否可用且不为空,否则,它将用作:测试(返回 true 的无操作实用程序)。

答案3

一些能够运行任意 shell 代码而不仅仅是运行的替代方法[ -f "$d/${d##*/}.md" ]

将 shell 代码作为第一个参数传递并用于eval解释它:

FIND(){
    find . -type d -exec sh -c '
        code=$1; shift
        for d do
            eval "$code" && printf "%s\n" "$d"
        done' find-sh "$1" {} +
}
FIND '[ -f "$d/${d##*/}.doc" ]'

与环境变量相同

FIND(){
    CODE=$1 find . -type d -exec sh -c '
        for d do
            eval "$CODE" && printf "%s\n" "$d"
        done' find-sh "$1" {} +
}
FIND 'base=${d##*/}; [ -f "$d/$base.md" ] && [ -f "$d/$base.doc" ]'

如果要解释的 shell 代码始终是[ -f "something" ]

FIND(){
    FILE_TEST=$1 find . -type d -exec sh -c '
        for d do
            eval "[ -f \"$FILE_TEST\" ]" && printf "%s\n" "$d"
        done' find-sh "$1" {} +
}
FIND '$d/${d##*/}.doc'

请注意,如果使用zsh,则可以仅使用其glob 限定符通过任意 shell表达式e进一步限定目录 ( ) :/e

print -rC1 ./**/*(ND/e['[ -f $REPLY/$REPLY:t.md ]'])

或者用一个函数:

has_md() [ -f $REPLY/$REPLY:t.md ]
print -rC1 ./**/*(ND/+has_md)

Nfor nullglob, Dfordotglob不忽略隐藏文件和目录)

相关内容