0

I am trying to use two variables in another command. For example,

var1=$(ls | grep "foo")
var2=$(ls | grep "bar")
common=$(comm -1 -2 <($var1) <($var2))

echo "$common" 

This is not working. When I write the entire thing in a single line it works.

Edit: I want to grep two file lists and then print only the common ones. But I still want the variables to be there since they are used in multiple places below.

3
  • Do you have some commands which names contain foo and bar in the current directory? Commented Jul 18, 2018 at 14:50
  • Is your sample code in any way representative of something you're actually trying to achieve? I.e. if you were to be offered a better way of comparing file lists, would that be helpful, or do you just want help with comm? Commented Jul 18, 2018 at 15:05
  • I edited the question to with more info Commented Jul 18, 2018 at 15:14

5 Answers 5

3

Bash variables are strings, not lazily evaluated expressions. If you want something which can be evaluated later, use functions.

var1() { ls | grep "foo"; }
var2() { ls | grep "bar"; }
common=$(comm -1 -2 <(var1) <(var2))

You could refactor that:

lsgrep() { ls | grep "$1"; }
common=$(comm -1 -2 <(lsgrep foo) <(lsgrep bar)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the explanation as well
2

Ignoring the problems of parsing ls and storing commands in variables, you want the actual command, not the result of the command, in your two variables.

var1='ls | grep foo'
var2='ls | grep bar'
common=$(comm -1 -2 <($var1) <($var))

Let's get rid of the variable first. Use functions to store code.

g1 () { ls | grep foo; }
g2 () { ls | grep bar; }

common=$( comm -1 -2 <(g1) <(g2) )

Now, let's get rid of ls, although this doesn't really solve the actual problem that parsing ls's output presents, which is failure to distinguish a single filename containing a newline from two separate filenames.

g1 () { printf '%s\n' *foo*; }
g2 () { printf '%s\n' *bar*; }

common=$( comm -1 -2 <(g1) <(g2) )

2 Comments

The last function just uses file globs of foo and bar right? So it will just the files having the pattern. But OP wants the filenames containing the pattern foo/bar?
True; I created patterns based on the literal examples. In theory, though, you can always replace a regular expression with an extended pattern, although the result may be prohibitively large. (.{2,5} would be something like @(??|???|????|?????); imagine replacing .{1,100}.
1

<(cmd) creates an anonymous file from cmd's output. Unless your variables contain commands (which is frowned upon) you should write <(printf %s "$var") to create an anonymous file with $var's content.

var1=$(ls | grep "foo")
var2=$(ls | grep "bar")
common=$(comm -1 -2 <(printf %s "$var1") <(printf %s "$var2"))

Also don't parse the output of ls. Use find or globbing. In your case, you could replace the whole script with

printf '%s\n' * | grep foo | grep bar

or even

printf '%s\n' *foo*bar* *bar*foo*

Comments

0

Given that it's generally accepted that you shouldn't parse the output ls, and you're using bash, I'd recommend using globbing and an array. Something like this perhaps:

$ touch foobar foo_and_bar bar_foo foo_alone just_bar
$ declare -A a b
$ for f in *foo*; do a["$f"]=1; done
$ for f in *bar*; do unset a["$f"]; done
$ declare -p a
declare -A a=([foo_alone]="1" )

or

$ printf '%q\n' "${!a[@]}"
foo_alone

This uses an associative array (which requires bash version 4 or above) to store filenames from the first set as index values. It then steps through the second set, removing files that match, obviously leaving anything that does not match.

The output of this is not a full diff, it's the sort of thing you'd get from comm -23, but it handles the comparison natively in bash rather than spawning an external program. You can swap the globs to tune this to make this function like comm -12. (But foo and bar are just placeholders anyway.)

Comments

-1

The least modification of the original version goes like this:

var1='ls|grep foo'
var2='ls|grep bar'
common=$(comm -1 -2 <(eval $var1) <(eval $var2))

The key is to use eval and postpone the evaluation of the vars.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.