I have to write a Bash script which checks if another Bash script contains a certain command line. Since Bash allows you to split a command line over multiple lines, my script must be able to merge the corresponding lines before the actual pattern matching can take place.How do I parse all multi-line into single-line commands in a Bash script?ExampleI want to check if a certain script contains the ls command - and if it contains the ls command, I want to know which parameters are passed to the ls command. To answer this question, I could use sed. But therefore I have to merge all multi-line commands first.Input:# My comment \ls \-a \-lOutput:# My comment \ls -a -lExample for an invalid output:# My comment ls -a -l
Bash: Parse multi-line into single-line commands
bash;shell script;scripting
Just before shellshock, I answered a question on StackOverflow about eliminating comments in bash scripts. My answer used the simple trick of creating a function by enclosing the contents of the script file inside tmp_() { ... }, and then using declare -f tmp_ to pretty-print the function. In the pretty-printed output, there are no comments and lines continued with a backslash-newline have been resolved to single lines. (Except inside backticked command substitution.)Some other reformatting is also done. For example, compound commands are split into several lines. And some forms of line continuation are not reformatted; for example, a line ending with a pipe symbol is not altered. But it should satisfy the use-case in this question. (See example output below.)Of course, the function definition needs to be evaluated, which means that the script being pretty-printed might include an injection attack. In the code I suggested, the function definition is evaluated by way of the bash feature which allows functions to be exported and shared with a child process. At the time I wrote this little hack, I believed that mechanism to be safer than calling eval, but as it turns out I was wrong. Since shellshock, there have been a number of improvements to the code bash uses to import function definitions, closing the door on at least some injection attacks, but there is clearly no assurance that the procedure is completely safe.If you are going to run the script being analyzed, then using this procedure to pretty-print it probably does not increase your vulnerability; an attacker could simply insert the dangerous code directly in the script and there would be no need to jump through hoops to hide the attack in a way which might bypass the safety checks in the function import code.All the same, you should think carefully about security issues, both with this little program and with whatever plans you might have to execute arbitrary scripts.Here is the version of the pretty-printer which works with a post-shellshock-patched bash (and will not work with previous bash versions):env BASH_FUNC_tmp_%%=() {$(<script_name)} bash -c 'declare -f tmp_' | tail -n+2Substitute the name of the file containing the script for script_name, in the second line. You might want to adjust the tail command; it removes the wrapper function name, but does not remove the braces which surround the script body.The original version, which will work on pre-shellshock versions of bash, can be found in the referenced SO answer.Sample.Tested against the input provided by Stphane Chazelas:{     echo \\;    echo a#b;    echo 'foo\bar';    cat  <<EOFthisis joinedthis 'aswell'$(ls -l)EOF    cat  <<'EOF'this is\not joinedEOF    echo $(ls -l);    echo `ls \\-l`}This differs from Stphane's suggested output:Lines have been indented, and many have been terminated with semicolons. Whitespace has been added and/or deleted in many lines.cat << E\OF has been changed to cat <<'EOF', which is semantically identical.The nested continuation line in the backticked command substitution at the end has not been modified. (The continuation line in the $(...) command substituion is eliminated.)