8

I want to run a bash command on output from Drupal's drush command-line interface. drush site-alias returns a list of webroots, first showing the name of the group, and then each site in that group. The site itself is aliased in the form group.site. For instance, you might get

internal internal.site1 internal.site2 external external.site1 external.site2 marketing marketing.site1 marketing.site2

I want to do a command on each of the site aliases, but not on the group alias itself. I need to test if the string has a period in it, and if so, run it:

for i in $(drush site-alias); do {if no period) drush $i command; done;

How can I run this test?

6 Answers 6

15

You can use pattern matching:

for i in $(drush site-alias) ; do
    if [[ $i == *.* ]] ; then
        drush "$i" command
    fi
done
7

With any POSIX-like shell, assuming you want to run the command if $i does not contain a period as your example suggests, you'd write it:

case $i in
  (*.*) ;; # contains "."s, do nothing
  (*) drush "$i" command;;
esac

Note that with $(drush site-alias) being left unquoted, split+glob is performed on it. The splitting, which you want, being done on characters of $IFS (space, tab and newline by default (plus NUL in zsh), and the globbing, which you don't want would turn a /* word for instance into all the non-hidden files or directories in /.

On a GNU system, and with a shell with support for Ksh-style process substitution such as the GNU shell (bash), and assuming that command outputs one site-alias per line, you could do:

xargs -rd '\n' -a <(drush site-alias | grep -vF .) -I@ drush @ command

That has several advantages:

  • no globbing
  • properly splits on newline and newline only regardless of what $IFS happens to contain
  • doesn't store the whole output in memory and starts to run drush commands as soon as drush site-alias starts to output something
  • if any of the drush command fails, the failure will be reflected on xargs's exit status.

Note that the failure of drush site-alias if any is still ignored.

With zsh, you could do:

autoload zargs
site_aliases=( ${(f)"$(drush site-alias)"} ) || exit
zargs -I@ -- ${site_aliases:#*.*} -- drush @ command

Where:

  • we also split on newline only by using the f parameter expansion flag (empty lines are discarded).
  • we handle the failure of drush site-alias (here by exiting the script)
  • ${site_aliases:#*.*} filters out (add a M parameter expansion flag to filter in instead) the elements that contain .
  • like xargs, zargs returns a failure exit status if and of the commands fails.
2

You can do this with a pattern replace expansion:

for i in $(drush site-alias); do
  if [ -z "${i//[^.]/}" ]; then
     # no period
     drush "$i" command
  fi
done

Yes, I quoted "$i", which is probably something you should do when possible, it avoids surprises. Though in this case it won't matter.

2
  • You don't have to quote it if you use [[ instead of [. You are already using non-POSIX features. Commented Feb 1, 2013 at 23:54
  • 1
    @jordanm No, I mean I quoted it in the drush "$i" line. Which you should quote regardless of using [[ vs. [. Commented Feb 2, 2013 at 1:48
2

Using the =~ bash operator.

for i in $(drush site-alias); do
  if ! [[ $i =~  \. ]]; then
     drush "$i" command
  fi
done
1
  • 1
    Note that with bash-3.1 (or with bash -O compat31, or with zsh), you'd need [[ $i =~ \\. ]] or [[ $i =~ '\.' ]]. For compatibility between the versions, you can use a variable (dot='\.'; [[ $i =~ $dot ]]) or use [[ $i =~ [.] ]] Commented Jul 30, 2016 at 8:46
2

Just for the sake of another independent answer, I'm going to suggest using shell parameter expansions.

for i in $(drush site-alias)
do
    if [[ ${i%.*} == $i ]]
    then
        drush $i command
    fi
done
3
  • 1
    I don't see any advantage of this over [[ $i = *.* ]], which ksh (even ksh88) also understands and is markedly clearer and marginally faster. Commented Feb 1, 2013 at 23:10
  • Like I wrote, just another answer: why leave something out, if stackexchange is to be a definitive Q&A site? Commented Feb 1, 2013 at 23:41
  • That would return true for i='*' (in which case the missing quoted around $i in drush $i... would also be a problem (would be a problem in any case)). Commented Jul 30, 2016 at 8:41
-1

Strip out all the periods with sed. Then see if the original string equals the new string.

s="your test string here."

if [ "$(echo "$s" | sed 's/\.//g')" == "$s" ]; then 
   echo "No periods in $s" 
else echo "Periods in $s"; fi
4
  • 2
    While it is possible to do this with sed (and many other tools), the original question asked for a bash solution, and it already has good answers, so adding a somewhat off-the spot answer 10 years later isn't really creating value :-) Commented Dec 21, 2023 at 9:20
  • 1
    Sed is commonly used in bash so I think you are splitting hairs. Many people still find answers in old questions while searching in Google so it is still relevant. Nice way to treat people on this site. Commented Dec 22, 2023 at 3:50
  • Yes, sed is often used in bash scripts, but also have to think about the tasks that sed is used for in scripts. Removing all dots in a string is something that bash is capable of doing without having to pass the string via echo (possibly modifying it in the process depending on what shell options are set and what the string contains) and spawning an external command. You are already using the pattern comparison operator ==, so why not use a pattern with it: "$s" == *.*? Commented Dec 22, 2023 at 18:48
  • And sed can do it just as well. People need to find better things to do than nitpick. Commented Dec 22, 2023 at 21:31

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.