Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

I've been having a problem the last six months or so when combining searches in bash. The exact example is related to Python programming but would apply to any kind of detection/manipulation.

Suppose I want a list of all Python files in the directory where I am. This is easy:

find . -name '*.py'

Now, suppose I'm only interested in those Python files which contain the string "psycopg", e.g. because I want to transition from the old PostgreSQL access library psycopg to psycopg2. Still easy enough:

grep -l  psycopg `find . -name '*.py'`

Now, suppose I want to do a full list of these files to inspect their date stamps to see when I last touched them. I could also wish to open them in gedit, but let's stick to the list:

ls -l `!!`

which expands to

ls -l `grep -l  psycopg `find . -name '*.py'``

Now, what happens? Answer: Nothing. The bash prompt just hangs. Why is that? This may be a wrong way of doing things, but it used to work. I'm pretty sure it worked pre- Ubuntu 10.10 or 10.04.

share|improve this question
    
fyi: I tested your last command on hardy and lucid and both hang up too ;) –  Rinzwind May 12 '11 at 13:00
add comment

4 Answers 4

up vote 6 down vote accepted

The problem is the nested command substitution with backticks. bash first tries to execute grep -l psycopg (which is between the first two backticks). But there's no filename - therefore it expects input on stdin (you can try this by entering psycopg).

You can avoid this problem by using the $(command) syntax for command substitutions:

grep -l  psycopg $(find . -name '*.py')
ls -l $(!!)

In this case it can be nested.

share|improve this answer
add comment

Backticks do not nest. The first embedded command is grep -l psycopg which is going to listen on stdin for data because there are no filenames.

I'd say get used to using $(this syntax) instead of backticks because they do nest properly.

ls -l $(grep -l psycopg $(find . -name \*py))
share|improve this answer
    
Thanks to you too. Funny thing is that, as I observe, they used to nest - or at least I think they did, I've been using this technique for years. But it's true, I acquired the habit of using backtips back from when I was still using tcsh. Must get used to $(...) instead. –  Carsten Agger May 12 '11 at 13:50
    
Upvote. Just a note: IIRC /bin/bash in default Ubuntu is in fact dash not real bash. That could be part of why they no longer nest properly. –  hbdgaf Apr 2 '12 at 0:26
2  
@aking1012, you mean /bin/sh is dash. –  glenn jackman Apr 2 '12 at 12:51
    
Yes. You are correct. –  hbdgaf Apr 2 '12 at 12:59
add comment

Do it in general the other way round, find the files, grep in them, and use finds ls:

find . -name '*.py' -exec grep -q psycopg {} ";" -ls

special forms of output are possible (printf) and listed in the manpage of find.

share|improve this answer
add comment

It is always wrong to have commands that output more than one filename inside $() or `` (command substitution). See http://mywiki.wooledge.org/ParsingLs for an explanation of why you shouldn't do that.

Also read http://mywiki.wooledge.org/UsingFind and http://mywiki.wooledge.org/BashFAQ/020 on how to use find and deal with filenames properly.

And no,

ls -l `grep -l  psycopg `find . -name '*.py'``

couldn't possibly have worked in earlier versions either.

user unknown's answer shows a good and safe way to do the task you were asking about.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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