为什么bash插入" ls /"的输出;在输出?

时间:2016-07-26 02:05:24

标签: php bash shell quoting

我在bash中遇到了一个相当神秘的错误,我怀疑它与shell扩展规则有关。

以下是故事:在工作中,我的任务是记录一个用于协调公司资源的大型内部网站。不幸的是,代码非常丑陋,因为它已经超出了它的原始目的而且已经发展了#34;进入协调公司努力的主要资源。

大部分代码都是PHP。我写了一些帮助脚本来帮助我编写文档;例如,一个脚本提取php函数中使用的所有全局php变量。

所有这些脚本的核心是" extract_function.sh"脚本。基本上,给定一个php函数名和一个php源文件,它提取并输出那个php函数。

现在问题出现了:不知何故,当脚本提取函数时,它基本上是在输出中随机插入ls /的输出。

例如:

$ ./extract_function my_function my_php_file.php
function my_function {
    // php code
/etc
/bin
/proc
...
   // more php code
}

更令人困惑的是,我只是从一个特定文件中获得了一个特定功能!现在,由于功能相当庞大(500多行,我的意思是当我说代码很丑!)时,我还没有能够让我的生活找出导致这种情况的原因,或者来使用更简单的ad-hoc函数来产生此行为。此外,公司政策阻止我分享实际代码。

但是,这是我的代码:

#!/usr/bin/env bash
program_name=$(basename $0);
function_name=$1;
file_name=$2;

if [[ -z "$function_name" ]]; then
    (>&2 echo "Usage: $program_name function_name [file]")
    exit 1
fi

if [[ -z "$file_name" ]] || [ "$file_name" = "-" ]; then
    file_name="/dev/stdin";
fi

php_lexer_file=$(mktemp)
trap "rm -f $php_lexer_file" EXIT
read -r -d '' php_lexer_text << 'EOF'
<?php
    $file = file_get_contents("php://stdin");
    $tokens = token_get_all($file);
    foreach ($tokens as $token) 
        if ($token === '{')
            echo PHP_EOL, "PHP_BRACKET_OPEN", PHP_EOL;
        else if ($token == '}')
            echo PHP_EOL, "PHP_BRACKET_CLOSE", PHP_EOL;
        else if (is_array($token))
            echo $token[1];
        else
            echo $token;
?>
EOF
echo "$php_lexer_text" > $php_lexer_file;

# Get all output from beginning of function declaration
extracted_function_start=$(sed -n -e "/function $function_name(/,$ p"  < $file_name);

# Prepend <?php so that php will parse the file as php
extracted_function_file=$(mktemp)
trap "rm -f $extracted_function_file" EXIT
echo '<?php' > $extracted_function_file;
echo "$extracted_function_start" >> $extracted_function_file;
tokens=$(php $php_lexer_file < $extracted_function_file);
# I've checked, and at this point $tokens does not contain "/bin", "/lib", etc...

IFS=$'\n';
open_count=0;
close_count=0;
for token in $tokens; do # But here the output of "ls /" magically appears in $tokens!
    if [ $token = "PHP_BRACKET_OPEN" ]; then
        open_count=$((open_count+1))
        token='{';
    elif [ $token == "PHP_BRACKET_CLOSE" ] ; then
        close_count=$((close_count+1))
        token='}';
    fi

    echo $token;
    if [ $open_count -ne 0 ] && [ $open_count -eq $close_count ]; then
        break;
    fi
done

是的,我知道我不应该使用bash来操纵php代码,但我基本上有两个问题:

1)为什么bash这样做?

2)而且,我该如何解决?

1 个答案:

答案 0 :(得分:6)

$tokens中的一个标记是 * (或者可以匹配多个文件的glob模式)。如果您不能安排令牌列表不包含shell元字符,则需要跳过一些箍以避免扩展。一种可能的技术是使用read -ra将令牌读入数组,这样可以更容易引用它们。