Question

How do I tell grep to only print out lines if the "filename" matches when I'm piping through ls? I want it to ignore everything on each line until after the timestamp. There must be some easy way to do this on a single command.

As you can see, without it, if I searched for the file "rwx", it would return not only the line with rwx.c, but also the first three lines because of permissions. I was going to use AWK but I want it to display the whole last line if I search for "rwx".

Any ideas?

EDIT: Thanks for the hacks below. However, it would be great to have a more bug-free method. For example, if I had a file named "rob rob", I wouldn't be able to use the stated solutions.

drwxrwxr-x 2 rob rob  4096 2012-03-04 18:03 .
drwxrwxr-x 4 rob rob  4096 2012-03-04 12:38 ..
-rwxrwxr-x 1 rob rob 13783 2012-03-04 18:03 a.out
-rw-rw-r-- 1 rob rob  4294 2012-03-04 18:02 function1.c
-rw-rw-r-- 1 rob rob   273 2012-03-04 12:54 function1.c~
-rw-rw-r-- 1 rob rob    16 2012-03-04 18:02 rwx.c
-rw-rw-r-- 1 rob rob    16 2012-03-04 18:02 rob rob
Was it helpful?

Solution

Why don't you use grep and match the file name following the timestamp?

grep -P "[0-9]{2}:[0-9]{2} $FILENAME(\.[a-zA-Z0-9]+)?$"

The [0-9]{2}:[0-9]{2} is for the time, the $FILENAME is where you'd put rob rob or rwx, and the trailing (\.[a-zA-Z0-9]+)? is to allow for an optional extension.

Edit: @JonathanLeffler below points out that when files are older than bout 6 months the time column gets replaced by a year - this is what happens on my computer anyhow. You could do ([0-9]{2}:[0-9]{2}|(19|20)[0-9]{2}) to allow time OR year, but you may be best of using awk (?).

[foo@bar ~/tmp]$ls -al
total 8
drwxrwxr-x  2 foo foo 4096 Mar  5 09:30 .
drwxr-xr-- 83 foo foo 4096 Mar  5 09:30 ..
-rw-rw-r--  1 foo foo    0 Mar  5 09:30 foo foo
-rw-rw-r--  1 foo foo    0 Mar  5 09:29 rwx.c
-rw-rw-r--  1 foo foo    0 Mar  5 09:29 tmp

[foo@bar ~/tmp]$export filename='foo foo'

[foo@bar ~/tmp]$echo $filename
foo foo

[foo@bar ~/tmp]$ls -al | grep -P "[0-9]{2}:[0-9]{2} $filename(\.[a-zA-Z0-9]+)?$"
-rw-rw-r--  1 cha66i cha66i    0 Mar  5 09:30 foo foo

(You could additionally extend to matching the whole line if you wanted:

^                              # start of line
[d-]([r-][w-][x-]){3} +        # permissions & space (note: is there a 't' or 's'
                               # sometimes where the 'd' can be??)
[0-9]+                         # whatever that number is
[\w-]+ [\w-]+ +                # user/group (are spaces allowed in these?)
[0-9]+ +                       # file size (modify for -h switch??)
(19|20)[0-9]{2}-               # yyyy (modify if you want to allow <1900)
(1[012]|0[1-9])-               # mm
(0[1-9]|[12][0-9]|3[012]) +    # dd
([01][0-9]|2[0-3]):[0-6][0-9] +# HH:MM (24hr)
$filename(\.[a-zA-Z0-9]+)?     # filename & optional extension
$                              # end of line

. You get the point, tailor to your needs.)

OTHER TIPS

The following will list only file name, and one file in each row.

$ ls -1     

To include . files

$ ls -1a 

Please note that the argument is number "1", not letter "l".

Assuming that you aren't prepared to do:

ls -ld $(ls -a | grep rwx)

then you need to exploit the fact that there are 8 columns with space separation before the file name starts. Using egrep (or grep -E), you could do:

ls -al | egrep "^([^ ]+ +){8}.*rwx"

This looks for 'rwx' after the 8th column. If you want the name to start with rwx, omit the .*. If you want the name to end with rwx, add a $ at the end. Note that I used double quotes so you could interpolate a variable in place of the literal rwx.

This was tested on Mac OS X 10.7.3; the ls -l command consistently gives three columns for the date field:

-r--r--r--  1 jleffler  staff   6510 Mar 17  2003 README,v
-r--r--r--  1 jleffler  staff  26676 Mar  3 21:44 ccs.nmd

Your ls -l seems to be giving just two columns, so you'd need to change the {8} to {7} for your machine - and beware migrating between systems.

Well, if you're working with filenames that don't have spaces in them, you could do something like this:

grep 'rwx\S*$'

Aside frrm the fact that you can use pattern matching with ls, exaple ksh and bash, which is probably what you should do, you can use the fact that filename occur in a fixed position. awk (gawk, nawk or whaever you have) is a better choice for this. If you have to use grep it smells like homework to me. Please tag it that way.

Assume the filename starting position is based on this output from ls -l in linux: 56

-rwxr-xr-x  1 Administrators None    2052 Feb 28 20:29 vote2012.txt

ls -l | awk ' substr($0,56) ~/your pattern even with spaces goes here/'

e.g.,

ls -l | awk ' substr($0,56) ~/^val/'

will find files starting with "val"

As a simple hack, just add a space before your filename so you don't match the beginning of the output:

ls -al | grep '\srwx'

Edit: OK, this is not as robust as it should be. Here's awk:

ls -l | awk ' $9 ~ /rwx/ { print $0 }'

This works for me, unlike ls -l & others as some folks pointed out. I like this because its really generic & gives me the base file name, which removes the path names before the file.

ls -1 /path_name |awk -F/ '{print $NF}'

Only one command you needed for this --

ls -al | gawk '{print $9}'

You can use this:

ls -p | grep -v / 

this is super old, but i needed the answer and had a hard time finding it. i didn't really care about the one-liner part; i just needed it done. this is down and dirty and requires that you count the columns. i'm not looking for an upvote here, just leaving some options for future searcher-ers.

the helpful awk trick is here -- Using awk to print all columns from the nth to the last

if YOUR_FILENAME="rob rob" and WHERE_FILENAMES_START=8

ls -al | while read x; do
  y=$(echo "$x" | awk '{for(i=$WHERE_FILENAMES_START; i<=NF; ++i) printf $i""FS; print ""}')
  [[ "$YOUR_FILENAME " = "$y" ]] && echo "$x"
done

if you save it as a bash script and swap out the vars with $2 and $1, throw the script in your usr bin... then you'll have your clean simple one-liner ;)

output will be:

> -rw-rw-r-- 1 rob rob    16 2012-03-04 18:02 rob rob

the question was for a one-liner so...

ls -al | while read x; do [[ "$YOUR_FILENAME " = "$(echo "$x" | awk '{for(i=WHERE_FILENAMES_START; i<=NF; ++i) printf $i""FS; print ""}')" ]] && echo "$x" ; done

(lol ;P)


on another note: mathematical.coffee your answer was rad. it didn't solve my version of this problem, so i didn't upvote, but i liked your regex breakdown :D

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top