Я столкнулся с довольно загадочной ошибкой в bash, которая, как я подозреваю, связана с правилами расширения оболочки.
Вот история: на работе мне было поручено документировать огромный внутренний веб-сайт для координации ресурсов компании. К сожалению, код довольно уродливый, поскольку он перерос свою первоначальную цель и «превратился» в основной ресурс для координации усилий компании.
Большая часть кода 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 строк, я имею в виду, когда я говорю, что код некрасив!), Я не смог за всю жизнь выяснить, что является причиной этого, или придумать более простая специальная функция для создания такого поведения. Кроме того, политика компании не позволяет мне делиться актуальным кодом.
Тем не менее, вот мой код:
#!/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) И как я могу это исправить?
Один из жетонов в $tokens
это * (или шаблон глобуса, который может соответствовать нескольким файлам). Если вы не можете организовать, чтобы список токенов не содержал метасимволов оболочки, вам нужно будет перепрыгнуть через некоторые обручи, чтобы избежать расширения. Одним из возможных методов является использование read -ra
читать токены в массив, что облегчит их цитирование.
Других решений пока нет …