Home > other >  Bash: stop variable expansion for function argument
Bash: stop variable expansion for function argument

Time:03-09

I am trying to write a bash script which has a function for logging:

log() {
   echo $1 2>&1
}

I checked the output of the following commands:

ARCHIVE_PREFIX=$2  // value = myFile
ARCHIVE_FILE_PATTERN="${ARCHIVE_PREFIX}-*.zip" // looking for zip archive starting with my file
log "Searching current directory for pattern - log: $ARCHIVE_FILE_PATTERN"  
echo "Searching current directory for pattern - echo: $ARCHIVE_FILE_PATTERN"

I would expect that the last 2 lines to print the same thing, but instead of that the output is:

Searching current directory for pattern - log: myFile-08022022.zip // an archive from current working dir
Searching current directory for pattern - log: myFile-*.zip

Can someone explain me why this happens and how can I change the logic so that the function will use the actual variable value?

CodePudding user response:

As Biffen said, it's because the $1 reference in log is unquoted. Here's a small example that reproduces the problem:

$ ls -l
total 0
-rw-r--r--  1 igstan  staff  0 Mar  9 12:55 bar.md
-rw-r--r--  1 igstan  staff  0 Mar  9 12:55 foo.md
$ PATTERN=*.md
$ echo "$PATTERN"
*.md
$ echo $PATTERN
bar.md foo.md

In your case, without quotes, echo $1 will:

  1. expand $1 to myFile-*.zip, which is a glob pattern
  2. expand the glob pattern to a list of files that match the pattern
  3. echo the list

So the fix is to quote it:

log() {
  echo "$1" 2>&1
}

In addition, I don't think you want to redirect stderr (2) to stdout (1), but probably the reverse, redirect stdout to stderr so that logging isn't part of the "normal" program output:

log() {
  echo "$1" 1>&2
}

However, if we test it with the previously-declared PATTERN variable, we'll see this happening:

$ log "$PATTERN"
*.md
$ log $PATTERN # glob pattern is expanded before being passed to `log`
bar.md

In the second case, only one of the filenames is printed out. This is because the glob expands to a list (loosely speaking) of filenames, so if you really want the flexibility to print the entire result, you'd have to swicth from $1 to $@ (all the function's arguments) inside the log function:

log() {
  echo "$@" 1>&2
}

We now have these results:

$ log "$PATTERN"
*.md
$ log $PATTERN # glob pattern is expanded before being passed to `log`
bar.md foo.md

CodePudding user response:

the variable you are trying to print is an array of all the results. you have to loop through the array to print each element something like this

for FILE in $ARCHIVE_FILE_PATTERN
do
log "Searching current directory for pattern - log: $FILE"  
echo "Searching current directory for pattern - echo: $FILE"
done

I'm not sure about the syntax of the bash loop but you get the idea you can rewrite it if this doesn't work.

  • Related