I've got the following shell script (let sayget_includes.sh):
#!/usr/bin/env bashincludes=($(grep ^#include file.c | grep -o '"[^"]\+"' | tr -d '"'))echo "0: ${includes[0]}"echo "1: ${includes[1]}"which aims at finding relative include files in source code file.
So for given file like this (file.c):
#include "foo.h"#include "bar.h"#include <stdio.h>int main(void) { printf("Hello World\n"); return 0;}It'll return the following results which are correct:
$ ./get_includes.sh 0: foo.h1: bar.hThe code works as expected, howevershellcheck complains about the following issues:
$ shellcheck get_includes.sh In get_includes.sh line 2: includes=($(grep ^#include file.c | grep -o '"[^"]\+"' | tr -d '"')) ^-- SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting). For more information: https://www.shellcheck.net/wiki/SC2207 -- Prefer mapfile or read -a to spli... https://www.shellcheck.net/wiki/SC2236 -- Use -n instead of ! -z.So:
- I can't quote command substitution, as I expect the command to expand to an array.
- I don't want to ignore the warning, I'd like to correct it.
I'm using Bash 4.
So, how I can correct the above line to satisfyshellcheck? If possible, I'd like to keep it in one-liner.
I've tried the following approaches which failed:
$ (grep ^#include file.c | grep -o '"[^"]\+"' | read -a myarr; echo $myarr)(nothing is printed)$ (grep ^#include file.c | grep -o '"[^"]\+"' | mapfile myarr; echo $myarr)(nothing is printed)2 Answers2
... so what's going on?
$ (grep ^#include file.c | grep -o '"[^"]\+"' | read -a myarr; echo $myarr)(nothing is printed)$ (grep ^#include file.c | grep -o '"[^"]\+"' | mapfile myarr; echo $myarr)(nothing is printed)
Why doesn't this work? Because"each command in a pipeline is executed in its own subshell". As the page explains, you could enable thelastpipe option to have the last element of the pipeline run in the current process. (The page doesn't mention that this will only work when you use it in a script, where job control is not active (as mentionedhere). It won't work in an interactive shell.)
Keep pipelines as short as possible
It's good to use the fewest possible processes in pipelines.Multiplegrep in a pipeline are points to look at with suspicion.You can make the firstgrep work a little harder,using a stricter pattern, and then you can achieve the same result with 2 processes instead of 3:
grep '^#include .*"' file.c | cut -d'"' -f2- \$\begingroup\$What would have worked is
... | { read -a myarr; echo "$myarr"; }but it would have made for quite ugly code.\$\endgroup\$Kusalananda– Kusalananda2019-03-25 08:10:44 +00:00CommentedMar 25, 2019 at 8:10
After reading more aboutSC2207, the proper syntax would be:
mapfile -t includes < <(grep ^#include file.c | grep -o '"[^"]\+"' | tr -d '"')You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.

