2

I've got the following shell script

#!/bin/bash

# Search elixir code with ack
# By default skip the dependencies and test directories
ignore_dirs=("dependencies" "test" "config")
# the for call below inserts newlines so we need to strip them out which is why the tr -d part is on the end
# of the assignment
ign_dirs="$(for i in "${ignore_dirs[@]}"; do echo "--ignore-dir=$i "; done | tr -d '\n')"

# By default skip the mix.exs file
ignore_files=("is:mix.exs" "is:credo.exs")
ign_files="$(for i in "${ignore_files[@]}"; do echo "--ignore-file=$i "; done | tr -d '\n')"

pager=less
file_types=elixir:ext:ex,exs,eex,heex


#echo ack $1 --word-regexp --pager="$pager" --type-set="$file_types" "$ign_dirs" "$ign_files" --noenv

# Array variation
ack $1 --word-regexp --pager="$pager" --type-set="$file_types" "$ign_dirs" "$ign_files" --noenv

# Hardcoded variation
ack $1 --word-regexp --pager=less --type-set=elixir:ext:ex,exs,eex,heex --ignore-dir=dependencies --ignore-dir=test --ignore-dir=config  --ignore-file=is:mix.exs --ignore-file=is:credo.exs  --noenv

I created the hardcoded variation with the commented out echo line. When I run the hardcoded variation ack works as expected. When I run the array variation it seems as if ack doesn't see the command line options--for example, mix.exs is included although it shouldn't be and it searches the test directory.

Is my shell script wrong? I copy/paste most of the shell script--I mean I can echo $ign_dirs and I see the right value there and when I run the command (the array variation) it doesn't work correctly which seems to indicate that my shell script is fine.

By the way, I'm running this on a mac on zsh. I did test this under bash and I see the same issue.

Apologies in advance if this is some sort of ack FAQ somewhere. I did check but didn't find anything to point to a solution.

EDIT:

I can see that I wasn't as clear as I could have been.

Ultimately I'm trying to automate the searching of Elixir code with ack. I want to keep my options/search within a bash script as much as possible so I won't have to worry about variations in .ackrc files and environment variables on different machines.

What I'm expecting is that when ack searches the files it will exclude the directories I've listed in ignore_dirs as well as skipping the files I've specified in the ignore_files. When I run my shell script with the hardcoded variation, the directories get ignored. When I run my shell script with the array variation they do not get ignored.

And yes, I know that ign_dirs and ign_files are strings not arrays. Is it possible to use the arrays directly on the command line in the shell script?

5
  • What is it supposed to achieve (what's its purpose)? Commented Jul 22, 2022 at 21:09
  • 2
    ign_files and ign_dirs aren't arrays in your implementation - they're strings. See How can we run a command stored in a variable? Commented Jul 22, 2022 at 21:09
  • "When I run the hardcoded variation ack works as expected" you've forgotten to tell us what is expected Commented Jul 22, 2022 at 21:27
  • 1
    Always paste your script into https://shellcheck.net, a syntax checker, or install shellcheck locally. Make using shellcheck part of your development process.
    – waltinator
    Commented Jul 22, 2022 at 22:12
  • @waltinator Valuable advice! I use shellcheck every time I have occassion to build a shell script because it's not my area of deepest knowledge! Commented Jul 25, 2022 at 12:52

2 Answers 2

4

Build arrays for the --ignore-* options:

ignore_dirs=("dependencies" "test" "config")
ign_dirs=()
for i in "${ignore_dirs[@]}"; do
  ign_dirs+=("--ignore-dir=$i");
done

ignore_files=("is:mix.exs" "is:credo.exs")
ign_files=()
for i in "${ignore_files[@]}"; do
  ign_files+=("--ignore-file=$i");
done

Then use those arrays in the command:

ack "$1" --word-regexp --pager="$pager" --type-set="$file_types" "${ign_dirs[@]}" "${ign_files[@]}" --noenv
1

Remove quotes around your variables in the final command.

ack $1 --word-regexp --pager="$pager" --type-set="$file_types" $ign_dirs $ign_files --noenv

You have multiple space separated pieces in these variables and you want them to be space separated.

The original code (with quotes) expands to:

ack $1 --word-regexp --pager=less --type-set=elixir:ext:ex,exs,eex,heex "--ignore-dir=dependencies --ignore-dir=test --ignore-dir=config" "--ignore-file=is:mix.exs --ignore-file=is:credo.exs"  --noenv

So it is not a set of keys with values, it is two positional parameters.

2
  • 1
    this will work only as long as the filenames themselves don't contain whitespace (or glob characters, or whatever IFS was set to)
    – ilkkachu
    Commented Jul 23, 2022 at 9:56
  • Thanks--this also works. But as @ilkkachu pointed out I surrounded the values in quotes because of potential whitespace. Commented Jul 25, 2022 at 12:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .