Short story

I'm looking for the command to enter first found foo-something directory like:

cd foo-*

but without using wildcard (or other special shell characters).

Long story

As part of the remote drush build script, I'm trying to find the way of entering folder which folder name could change, but it has common prefix. Same example: drush -y dl ads or drush -y dl ads --dev downloads either ads-7.x-1.0-alpha1 or ads-7.x-1.x-dev).

To make the things more tricky, the command can't consist either wildcard or escaped semicolon, because drush is heavily escaping shell aliases.

So ls * is escaped into ls '\''*'\''' and ending up with Command ls '*' failed. error.

I've tried also using find, but I can't use -exec primary, because semicolon needs to be escaped, and drush is double escaping it into ('\''\;'\'').

Therefore I'm looking to enter foo-* folder without using wildcard (or any other special characters, parameter expansion, command substitution, arithmetic expansion, etc.) if possible.

I believe the logic of shell escaping is here and it is intended to work the same way that escapeshellarg() does on Linux. What it does, it's escaping each parameter.

  • Does drush allow the use of shell functions? In other words, can you, either on a command line or in a config script, define the shell function cdstar() { cd "$1"*; } and then have access to it under drush? – John1024 Oct 17 '14 at 20:40
  • @John1024: Probably not (some examples), but I'll double check. – kenorb Oct 17 '14 at 20:46
  • @John1024: Drush shell aliases are very similar to git aliases. In drush you can only use shell alias replacements, but only to use with site alias names (like '#live' => '@acme.live'), so you can't put any code there and there are no any other variable replacements features. When I've tried your suggestion, it was escaped into cdstar() '\''{'\'' cd '\''$1*;'\'' '\''};'\'' cdstar foo, generating shell syntax error. – kenorb Oct 17 '14 at 20:56
  • OK. Too bad. Just to be clear, cdstar is a shell function, not an alias. Since the * has to come after the argument, making an alias is not sufficient. – John1024 Oct 17 '14 at 21:06
  • 1
    You can reliably shell escape a shell command with alias. For example: shell_quote() { set -- "$(alias "cmd=$1"; alias cmd)"; printf %s\\n "${1#*=}"; }; escaped=$(shell_quote 'ls *'); printf 'double_escaped=%s\n' "$(shell_quote "$escaped")" – mikeserv Oct 18 '14 at 19:40

Does drush mung backticks and vertical bars?  If not, you could use

cd `ls | grep foo- | head -n 1`

If backticks don't work, but |, $, ( and ) do, then you could change the above to

cd $(ls | grep foo- | head -n 1)

If | doen't work, but $, ( and ) do, then you could do

cd $(myprog)

where myprog is a script that you write to determine the directory name.

Also -- I don't understand how you might be able to use find to help you do a cd, but, can you end your -exec with a +?

|improve this answer|||||
  • Thanks, I'll try your solutions, head idea sounds promising. Re: find, I've tried something like: find . -name build.xml -print -exec echo phing -f {} \; -quit (where build.xml is one of the files within the folder) or something like find . -name build.xml -exec cd {} \; -quit (it's not complete, because path needs to be converted into dir), but it won't work anyway, because after run semicolon was escaped twice breaking the syntax. – kenorb Oct 17 '14 at 22:06
  • Well, the point I was making in my last sentence was that find ... -exec cd {} \; isn't going to work, since cd is a shell builtin command and not an executable program. You might be able to do something like find ... -exec sh -c "cd $1; (other commands)" -- {} +, but that's starting to get messy. – G-Man Says 'Reinstate Monica' Oct 17 '14 at 22:12
  • 2
    @DopeGhoti: (1) Does IFS="\n" really do any good in your suggested command? (2) Haven’t you heard that deferred wildcards/globs (i.e., arguments to -name and -iname in find) should always be quoted? (3) Did you miss the part of the question where the OP said that he couldn’t use *? (4) find … | head -n1 chops up names with newlines in them just as badly as ls | head -n 1. … (cont’d) – G-Man Says 'Reinstate Monica' Oct 18 '14 at 1:05
  • 1
    (cont’d) … (5) My version of head does not have a -d, -p, -e, -t, or -h option. Perhaps you meant to say -depth instead of -dpeth? And perhaps you meant to include it in the find command rather than tacking it on to the head command? (6) I’ve heard the tale about parsing the output of ls, but extraordinary times call for extraordinary measures. The OP seems to believe that he has a general idea of what his directory names are going to look like, and, after four hours, nobody else has posted an answer that tries to comply with the constraints of the question. – G-Man Says 'Reinstate Monica' Oct 18 '14 at 1:06
  • 1
    Parsing ls, unanchored grep, backticks, oh my. – l0b0 Oct 18 '14 at 8:24

You may also try using locate utility, e.g.:

updatedb -o ~/tmp.db -l0 -U $PWD; cd $(locate -d ~/tmp.db -l 1 -b -r foo-)

For more complex regex pattern check info locate.

|improve this answer|||||

Based on the final requirement you don't need cd; you can do the following:

find . -type d -name 'foo-*' -exec make -C {} ';'


find . -type d -name 'foo-*' -exec phing -f {}/build.xml ';'

The asterisks are handled by find internally, and I believe this is POSIX compatible.

|improve this answer|||||
  • Thanks for the answer, unfortunatelly the problem is with escaped semicolon as already mentioned, it gets double escaped into '\''\;'\'' and breaking the whole syntax. If I won't escape the semicolon, find command would complain. – kenorb Oct 18 '14 at 10:40
  • Tested 1st example: "!find . -type d -name 'foo-*' -exec make -C {} \;", it's get escaped as find . -type d -name '\'''\''\'\'''\''foo-*'\''\'\'''\'''\'' -exec make -C '\''{}'\'' '\''\;'\''' and parsed on the remote shell as: find . -type d -name ''\''foo-*'\''' -exec make -C '{}' '\;'. – kenorb Oct 18 '14 at 11:00
  • 1
    Similar to the 2nd: find . -type d -name 'foo-*' -exec phing -f {}/build.xml \;, parsed on the remote shell as: find . -type d -name ''\''foo-*'\''' -exec phing -f '{}/build.xml' '\;'. I don't think drush is following any POSIX compability, it's just escaping everything when you're trying to be smarter. – kenorb Oct 18 '14 at 11:02
  • You can quote the semicolon instead of escaping it. Updated the answer. – l0b0 Oct 18 '14 at 11:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.