1

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.

10
  • 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, 2014 at 20:40
  • @John1024: Probably not (some examples), but I'll double check.
    – kenorb
    Oct 17, 2014 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, 2014 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, 2014 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, 2014 at 19:40

3 Answers 3

0

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 +?

10
  • 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, 2014 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. Oct 17, 2014 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) Oct 18, 2014 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. Oct 18, 2014 at 1:06
  • 1
    Parsing ls, unanchored grep, backticks, oh my.
    – l0b0
    Oct 18, 2014 at 8:24
0

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.

0

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

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

and

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

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

4
  • 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, 2014 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, 2014 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, 2014 at 11:02
  • You can quote the semicolon instead of escaping it. Updated the answer.
    – l0b0
    Oct 18, 2014 at 11:19

You must log in to answer this question.

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