Hi all,
with Bash one can list directories (excluding dot dirs) like this:
ls [^.]*/
How can I list files instead of directories with Bash? I thought this would be trivial but I can't find a solution anywhere.
Thanks.
____________________________________________________________________________________ It's here! Your new message! Get new email alerts with the free Yahoo! Toolbar. http://tools.search.yahoo.com/toolbar/features/mail/
On 1/28/07, Daniel Qarras dqarras@yahoo.com wrote:
Hi all,
with Bash one can list directories (excluding dot dirs) like this:
ls [^.]*/
How can I list files instead of directories with Bash? I thought this would be trivial but I can't find a solution anywhere.
Thanks.
I'm not on my Linux partition so I'm going by memory here, but something like:
ls -lAR / | grep -v "^d"
That will list Almost all files (exclude . and ..), Recursive, long format, then exclude anything that is a directory (as those have the d in front of the permissions).
Jacques B.
Hi!
--- "Jacques B." jjrboucher@gmail.com wrote:
On 1/28/07, Daniel Qarras dqarras@yahoo.com wrote:
Hi all,
with Bash one can list directories (excluding dot dirs) like this:
ls [^.]*/
How can I list files instead of directories with Bash? I thought
this
would be trivial but I can't find a solution anywhere.
Thanks.
I'm not on my Linux partition so I'm going by memory here, but something like:
ls -lAR / | grep -v "^d"
That will list Almost all files (exclude . and ..), Recursive, long format, then exclude anything that is a directory (as those have the d in front of the permissions).
Yep, that would work, but I guess my question was a bit poorly formulated. I am writing a bash script that has:
dirs=[^.]*/
and I'd like to have "files=..." without using find or other external commands if at all possible.
Thanks.
____________________________________________________________________________________ TV dinner still cooling? Check out "Tonight's Picks" on Yahoo! TV. http://tv.yahoo.com/
Yep, that would work, but I guess my question was a bit poorly formulated. I am writing a bash script that has:
dirs=[^.]*/
and I'd like to have "files=..." without using find or other external commands if at all possible.
Thanks.
Actually that last one wasn't ideal. A better solution would be ls -lAR / | grep "^-" This would give you files, not symbolic links, not directories, not block devices, not character devices.
I can't find any way to do it with ls alone.
Of course the best way is with the find command, being
find / -type f
which will not only give you the file names, but with their complete path. If you want the files listed in long format, then use
find / -type f -ls
By the way the syntax you have to list directories does not work for me. It yields the same results as if I did ls *, which lists all files in the current directory, and files in directories contained in the current directory (in lieu of listing the directory name).
Any reason why you'd rather use ls instead of find?
Thanks,
Jacques
Hi!
Yep, that would work, but I guess my question was a bit poorly formulated. I am writing a bash script that has:
dirs=[^.]*/
and I'd like to have "files=..." without using find or other
external
commands if at all possible.
Thanks.
Of course the best way is with the find command, being
find / -type f
Any reason why you'd rather use ls instead of find?
I want just the files from the current directory, not from any subdir. And I'd prefer some bash globbing if possible for performance/elegancy reasons :)
Thanks.
____________________________________________________________________________________ Need a quick answer? Get one in minutes from people who know. Ask your question on www.Answers.yahoo.com
Daniel Qarras wrote:
Hi!
Yep, that would work, but I guess my question was a bit poorly formulated. I am writing a bash script that has:
dirs=[^.]*/
and I'd like to have "files=..." without using find or other
external
commands if at all possible.
Thanks.
Of course the best way is with the find command, being
find / -type f
Any reason why you'd rather use ls instead of find?
I want just the files from the current directory, not from any subdir. And I'd prefer some bash globbing if possible for performance/elegancy reasons :)
Find has more options than you want to know about:
find . -maxdepth 1 -type f -name 'pattern'
Don't forget to quote 'pattern' so the shell won't expand it.
Or you could do something like:
ls -ld pattern |sed -e '/^d/d' -e 's/.* //' (remove the lines starting with d and all the stuff before the name).
The only way to do it without an external command would be to expand the list in the shell and cycle though the list eliminating the directories with a [ -d $VAR ] test. See 'man test' for the options, but it really is a shell built-in.
Daniel Qarras wrote:
I want just the files from the current directory, not from any subdir. And I'd prefer some bash globbing if possible for performance/elegancy reasons :)
Les Mikesell wrote:
The only way to do it without an external command would be to expand the list in the shell and cycle though the list eliminating the directories with a [ -d $VAR ] test. See 'man test' for the options, but it really is a shell built-in.
That won't work reliably, either -- directories can contain more things than plain files or subdirectories. There could be symbolic links, character or block device nodes, named pipes or sockets.
Daniel should probably briefly consider what he wants to do with any of these, and program around them. (I suspect "following symbolic links and ignoring the rest" may be a good heuristic".)
Hope this helps,
James.
On Sun, Jan 28, 2007 at 12:12:11PM -0800, Daniel Qarras wrote:
Hi all,
with Bash one can list directories (excluding dot dirs) like this:
ls [^.]*/
How can I list files instead of directories with Bash? I thought this would be trivial but I can't find a solution anywhere.
I don't think that it can be done with bash, but if you don't mind switching to zsh -- another extended version of the Bourne shell -- it does become trivial:
~% ls *(.) # list only plain files ~% ls *(/) # list only directories ~% ls *(*) # list only executable files
-- John Kodis.
On 1/29/07, James Wilkinson fedora@aprilcottage.co.uk wrote:
Daniel Qarras wrote:
I want just the files from the current directory, not from any subdir. And I'd prefer some bash globbing if possible for performance/elegancy reasons :)
The closest I could come up with was using ls -Ap -I "pattern"
That caused directories to have / at the end of them. So I figured I could use -I to ignore any filename ending with /. However after a few unsuccessfull variations I realized that the / is only displayed at the end of directories therefore not part of the name. Which means you cannot use a pattern to eliminate names ending in / (because the name doesn't end with /, rather / is simply displayed after a directory name). Piping to grep would do it of course.
The find command with maxdepth and for type f is your best bet in my opinion.
If you come up with a way to do it from within ls I'd be interested in it.
Now lsattr comes close as well in that it lists files, but also symbolic links to a directory. But that produces the attributes along with the file names.
Like John, I can see no way on how to do it with ls alone from within bash. Clearly John has a solution for you using another shell so you could call upon that shell interpreter. But I don't see zsh on my default FC6 install.
Jacques B.
Jacques B.
with Bash one can list directories (excluding dot dirs) like this:
ls [^.]*/
How can I list files instead of directories with Bash? I thought
this
would be trivial but I can't find a solution anywhere.
I don't think that it can be done with bash, but if you don't mind switching to zsh -- another extended version of the Bourne shell -- it does become trivial:
~% ls *(.) # list only plain files ~% ls *(/) # list only directories ~% ls *(*) # list only executable files
Yes, I am painfully aware of the power of zsh :) But in this case I need to do my script with bash and it seems that although globbing for directories is trivial ( */ ) there is no similar pattern to match files. Oh, well, I guess I'll need to waste some CPU cycles and launch find everytime I need to know file names in the current directory.
Thanks.
____________________________________________________________________________________ Sucker-punch spam with award-winning protection. Try the free Yahoo! Mail Beta. http://advision.webevents.yahoo.com/mailbeta/features_spam.html
Yes, I am painfully aware of the power of zsh :) But in this case I need to do my script with bash and it seems that although globbing for directories is trivial ( */ ) there is no similar pattern to match files.
I don't know if it's just me, but I tried your syntax to list directories and that doesn't work for me. You use: $ ls [^.]*/ correct? All that does on my system is list all files, including files in any subfolders within the current directory. Basically seems to do the exact same as if I do: $ ls *
Jacques B.
Hi!
--- "Jacques B." jjrboucher@gmail.com wrote:
I don't know if it's just me, but I tried your syntax to list directories and that doesn't work for me. You use: $ ls [^.]*/ correct? All that does on my system is list all files, including files in any subfolders within the current directory. Basically seems to do the exact same as if I do: $ ls *
It works for me both on FC6 (bash3) and on Debian Sarge (bash2). Perhaps there is something different in our inputrc/bashrc.
Thanks.
____________________________________________________________________________________ Sucker-punch spam with award-winning protection. Try the free Yahoo! Mail Beta. http://advision.webevents.yahoo.com/mailbeta/features_spam.html
On Sun, 2007-01-28 at 12:12 -0800, Daniel Qarras wrote:
Hi all,
with Bash one can list directories (excluding dot dirs) like this:
ls [^.]*/
How can I list files instead of directories with Bash? I thought this would be trivial but I can't find a solution anywhere.
Daniel,
My knowledge of bash is very limited, but I believe that the -d conditional checks if a file is a directory. So you could define a function something like this (the syntax is probably all wrong, but hopefully it is decipherable):
for file in `ls -1`; do if ![ -d file]; then printf "$file\n" fi end
Name the function something like "lsfiles" and you're good to go.
On Monday, Jan 29th 2007 at 12:14 -0800, quoth Evan Klitzke:
=>My knowledge of bash is very limited, but I believe that the -d =>conditional checks if a file is a directory. So you could define a =>function something like this (the syntax is probably all wrong, but =>hopefully it is decipherable): => =>for file in `ls -1`; do => if ![ -d file]; then => printf "$file\n" => fi =>end => =>Name the function something like "lsfiles" and you're good to go.
lsfiles () { typeset file typeset files
(( $# > 1 )) && echo 'Extra args ignored.' 1>&2 [[ -z "$1" ]] && files='*' || files="$@" for file in $files do [ -d $file ] || ls -d $file done }
Here's one just for fun. If people are having varying results then look at the various variables that control globbing. None of them will be found in the inputrc ;-)
On Mon, 2007-01-29 at 12:14 -0800, Evan Klitzke wrote:
On Sun, 2007-01-28 at 12:12 -0800, Daniel Qarras wrote:
Hi all,
with Bash one can list directories (excluding dot dirs) like this:
ls [^.]*/
How can I list files instead of directories with Bash? I thought this would be trivial but I can't find a solution anywhere.
Daniel,
My knowledge of bash is very limited, but I believe that the -d conditional checks if a file is a directory. So you could define a function something like this (the syntax is probably all wrong, but hopefully it is decipherable):
for file in `ls -1`; do if ![ -d file]; then printf "$file\n" fi end
Name the function something like "lsfiles" and you're good to go.
Uh, how about "find . -maxdepth 1 -type f -print" to list the files and "find . -maxdepth 1 -type d -print" for directories? Take out the "-maxdepth 1" and it'll walk the directory tree.
Nah, too prosaic!
---------------------------------------------------------------------- - Rick Stevens, Senior Systems Engineer rstevens@vitalstream.com - - VitalStream, Inc. http://www.vitalstream.com - - - - Make it idiot proof and someone will make a better idiot. - ----------------------------------------------------------------------
On Mon, 2007-01-29 at 14:38 -0800, Rick Stevens wrote:
Uh, how about "find . -maxdepth 1 -type f -print" to list the files and "find . -maxdepth 1 -type d -print" for directories? Take out the "-maxdepth 1" and it'll walk the directory tree.
Nah, too prosaic!
Next you'll be telling me that "cat foo | grep bar" is redundant!
-- Evan Klitzke
On 29Jan2007 10:18, Daniel Qarras dqarras@yahoo.com wrote: | Yes, I am painfully aware of the power of zsh :)
Please, come to the Dark Side with us!
| But in this case I | need to do my script with bash and it seems that although globbing for | directories is trivial ( */ ) there is no similar pattern to match | files. Oh, well, I guess I'll need to waste some CPU cycles and launch | find everytime I need to know file names in the current directory.
Gah! No!
files= for f in * do [ -f "$f" ] && files="$files $f" done ... do stuff with $files ...
If you've got to deal with filenames with whitespace in them you need to be trickier.
BTW, the above works in plain Bourne shell - no zsh or bash extensions needed.
On 29Jan2007 13:57, Jacques B. jjrboucher@gmail.com wrote: | >Yes, I am painfully aware of the power of zsh :) But in this case I | >need to do my script with bash and it seems that although globbing for | >directories is trivial ( */ ) there is no similar pattern to match | >files. | | I don't know if it's just me, but I tried your syntax to list | directories and that doesn't work for me. | You use: $ ls [^.]*/ | correct? | All that does on my system is list all files, including files in any | subfolders within the current directory. Basically seems to do the | exact same as if I do: $ ls *
Got an alias for "ls"? Try:
unalias ls ls -d */
The -d says don't treat directories specially (normally ls lists directory _contents_ if a directory is named).
Got an alias for "ls"? Try:
unalias ls ls -d */
The -d says don't treat directories specially (normally ls lists directory _contents_ if a directory is named).
I do have an alias for ls, but it's simply to add color. I did the unalias and get the same results. Now with the one you have listed here, I do get the directories. Even when I source my .bashrc back to get the alias back, it still works as expected. So the alias is not affecting my ls beyond adding color.
Is the command $ls [^.]*/ working as expected for you, listing only directories in your current directory?
I'm fairly comfortable with the basics of bash and some intermediate bash but I hadn't played with globbing much. In reading up on it I'm left with the impression that the command above would list all files not starting with a period ([^.]), then the wildcard * (so could be anything at all), followed by the / (which doesn't seem to do anything).
Even if it did work, would it not exclude any directory starting with a period? But that aside, does it work (or not work) for anyone else?
I have
GNU bash, version 3.1.17(1)-release (i686-redhat-linux-gnu)
Thanks,
Jacques
On 29Jan2007 18:58, Jacques B. jjrboucher@gmail.com wrote: | >Got an alias for "ls"? Try: | > unalias ls | > ls -d */ | > | >The -d says don't treat directories specially (normally ls lists | >directory _contents_ if a directory is named). | | I do have an alias for ls, but it's simply to add color. I did the | unalias and get the same results. Now with the one you have listed | here, I do get the directories. Even when I source my .bashrc back to | get the alias back, it still works as expected. So the alias is not | affecting my ls beyond adding color. | | Is the command $ls [^.]*/ | working as expected for you, listing only directories in your current | directory?
Yep, in both zsh and bash. BTW, what is the "[^.]" supposed to achieve? A plain "*" should act the same, I'd expect.
Try this:
set -x ls [^.]*/ ls -d [^.]*/ ls -d */
It would be interesting to see what the differences are, if any.
You could whack a ">/dev/null" onto the end of the ls commands so the "set -x" tracing is easy to see.
| I'm fairly comfortable with the basics of bash and some intermediate | bash but I hadn't played with globbing much. In reading up on it I'm | left with the impression that the command above would list all files | not starting with a period ([^.]), then the wildcard * (so could be | anything at all),
Yes, but plain "*" is supposed to not match things beginning with "." anyway.
| followed by the / (which doesn't seem to do | anything).
Yes it does - it forces a path traversal. You can do that with a directory, but not a file (nothing to transverse "into").
| Even if it did work, would it not exclude any directory starting with | a period?
Yes. I suspect it's a mistake from people trying to avoid "." and "..".
| But that aside, does it work (or not work) for anyone else? | | I have | GNU bash, version 3.1.17(1)-release (i686-redhat-linux-gnu)
Me too.
Try this:
set -x ls [^.]*/ ls -d [^.]*/ ls -d */
It would be interesting to see what the differences are, if any.
Thanks for the tip & additional info. The results of the above after doing unalias ls and set -x:
(directory names neutered slightly to protect the youngs)
ls [^.]*/ + ls 'My son'''s Passion/' Desktop/ download/ 'Final Material/' ftools/ images/ 'Linux Forensic Documents/' LPIC/ 'Other son - Profession/' Photos/ PicasaDocuments/ Presentations/ scripts/ thumb/
followed by the content of each of the above folders
The other two yield identical results, being:
+ ls -d 'My son'''s Passion/' Desktop/ download/ 'Final Material/' ftools/ images/ 'Linux Forensic Documents/' LPIC/ 'Other son - Profession/' Photos/ PicasaDocuments/ Presentations/ scripts/ thumb/
followed by the directory names above.
I like that trace feature - lets you see how the globbing gets expanded. So we see that the original syntax yields the content of the directories, whereas as expected the -d only lists the directory names.
Not to beat a dead horse, but I hate not understanding why something works (or doesn't work). Why is it working on yours but not mine? Are you getting the same trace output for the ls [^.]*/ command, but without listing the contents of those directories, only the directory names?
Thanks,
Jacques B.
On 29Jan2007 21:29, Jacques B. jjrboucher@gmail.com wrote: | >Try this: | > | > set -x | > ls [^.]*/ | > ls -d [^.]*/ | > ls -d */ | > | >It would be interesting to see what the differences are, if any. | | Thanks for the tip & additional info. The results of the above after | doing unalias ls and set -x: | | (directory names neutered slightly to protect the youngs) | | ls [^.]*/ | + ls 'My son'''s Passion/' Desktop/ download/ 'Final Material/' | ftools/ images/ 'Linux Forensic Documents/' LPIC/ 'Other son - | Profession/' Photos/ PicasaDocuments/ Presentations/ scripts/ thumb/ | | followed by the content of each of the above folders
So, as expected then, yes?
| The other two yield identical results, being: | | + ls -d 'My son'''s Passion/' Desktop/ download/ 'Final Material/' | ftools/ images/ 'Linux Forensic Documents/' LPIC/ 'Other son - | Profession/' Photos/ PicasaDocuments/ Presentations/ scripts/ thumb/ | | followed by the directory names above.
Good, also as expected.
| I like that trace feature - lets you see how the globbing gets | expanded. So we see that the original syntax yields the content of | the directories, whereas as expected the -d only lists the directory | names. | Not to beat a dead horse, but I hate not understanding why something | works (or doesn't work). Why is it working on yours but not mine? | Are you getting the same trace output for the ls [^.]*/ command, but | without listing the contents of those directories, only the directory | names?
I get what you get. We have established A) that "*/" and "[^.]*/" are equivalent and B) that -d prevents burrowing into the directories. We should have added:
ls */
to the tests above to try all four permutations.
Does it make sense now?
So, as expected then, yes?
Yes, as expected. But not what I understood the OP was trying to do. Unless I'm mistaken, that syntax (ls [^.]*/) was used to assign the directory names in the current working directory to a variable. However that is not the output of that command. The output is the directories and their contents. Hence where much of my confusion came.
Does it make sense now?
Yes, it finally clicked this morning around 5:00 am when I was driving. I now understand the reason for the */ part of the syntax.
Ultimately the OP's wishes would be best/most accurately met using the find command from what I can gather.
dirs=`find -maxdepth 1 -type d ! -regex "^.$"` (the ! -regex "^.$" to eliminate the . directory) files=`find -maxdepth 1 -type f`
Or to remove the path name (./) to have only the file names (or directory names), I would use: files=`find -maxdepth 1 -type f -printf "%f "` resulting in a space delimited output.
Jacques B.
Hi!
| But in this case I | need to do my script with bash and it seems that although globbing for | directories is trivial ( */ ) there is no similar pattern to match | files. Oh, well, I guess I'll need to waste some CPU cycles and launch | find everytime I need to know file names in the current directory.
Gah! No!
files= for f in * do [ -f "$f" ] && files="$files $f" done ... do stuff with $files ...
If you've got to deal with filenames with whitespace in them you need to be trickier.
Thanks, I'll be using this one.
PS. A hard headed zsh user would continue to complain that there's no way to differ between files and links in bash without find but I won't go into that :)
Cheers!
____________________________________________________________________________________ Sucker-punch spam with award-winning protection. Try the free Yahoo! Mail Beta. http://advision.webevents.yahoo.com/mailbeta/features_spam.html
files= for f in * do [ -f "$f" ] && files="$files $f" done ... do stuff with $files ...
If you've got to deal with filenames with whitespace in them you need to be trickier.
I like how this works. But you are right, it does cause a problem for filenames with spaces. As far as cpu time (preceding the command with "time" to get same), we are only talking a few 1/100th of a second difference with the find command. The find command is more robust as it will properly deal with filenames with spaces. Not to mention find will also yield hidden files (i.e. .file) whereas the above won't. All depends on your needs as to which will serve you better.
I've enjoyed this thread. Anytime I can learn something new about BASH then it's all good.
Jacques B.
On Tue, 30 Jan 2007, Jacques B. wrote:
files= for f in * do [ -f "$f" ] && files="$files $f" done ... do stuff with $files ...
If you've got to deal with filenames with whitespace in them you need to be trickier.
I like how this works. But you are right, it does cause a problem for filenames with spaces. As far as cpu time (preceding the command with "time" to get same), we are only talking a few 1/100th of a second difference with the find command. The find command is more robust as it will properly deal with filenames with spaces. Not to mention find will also yield hidden files (i.e. .file) whereas the above won't. All depends on your needs as to which will serve you better.
Note, that find also solves another problem: namely, that if the globbing generates a line length beyond the max for bash (in either the for or the concatenation), then the script will fail. This won't happen with find.
Steve Friedman
On 30Jan2007 09:08, Daniel Qarras dqarras@yahoo.com wrote: | PS. A hard headed zsh user would continue to complain that there's no | way to differ between files and links in bash without find but I won't | go into that :)
A POSIX shell requires that the test command supports this:
-h file True if file exists and is a symbolic link. -L file True if file exists and is a symbolic link.
There are two letters for historic reasons. Bash supports both, and so does zsh.
Of course, if you mean a hardlink, then you're asking a question that is meaningless.
BTW, I tend to test directorishness like this:
[ -d foo/. ]
which works for a directory or a symlink that resolves to a directory.
On 30Jan2007 15:24, Jacques B. jjrboucher@gmail.com wrote: | >> files= | >> for f in * | >> do [ -f "$f" ] && files="$files $f" | >> done | >> ... do stuff with $files ... | >> | >> If you've got to deal with filenames with whitespace in them you need | >> to be trickier. | | I like how this works. But you are right, it does cause a problem for | filenames with spaces. As far as cpu time (preceding the command with | "time" to get same), we are only talking a few 1/100th of a second | difference with the find command.
No. For _one_ run the cost is small. If something calls this code many times, find is MANY MANY times more costly that an in-shell forkless piece of code.
| The find command is more robust as | it will properly deal with filenames with spaces. Not to mention find | will also yield hidden files (i.e. .file) whereas the above won't.
This depends what you want. But yes. But you can do this:
for f in .* * do case "$f" in . | .. ) continue ;; esac
which gets it all.
On 30Jan2007 16:43, Steve Friedman steve@adsi-m4.com wrote: | Note, that find also solves another problem: namely, that if the globbing | generates a line length beyond the max for bash (in either the for or the | concatenation), then the script will fail. This won't happen with find.
That's pretty big. Note that the for loop is _not_ subject to the command line exec argument length limit, because no program is being exec()ed.
Cameron Simpson wrote:
On 30Jan2007 15:24, Jacques B. jjrboucher@gmail.com wrote:
[...]
| The find command is more robust as it will properly deal with | filenames with spaces. Not to mention find will also yield hidden | files (i.e. .file) whereas the above won't.
This depends what you want. But yes. But you can do this:
for f in .* * do case "$f" in . | .. ) continue ;; esac
which gets it all.
With bash you can also use "shopt -s dotglob" to allow * to match filenames beginning with a dot. I find that one handy. :)
Hi!
--- Cameron Simpson cs@zip.com.au wrote:
A POSIX shell requires that the test command supports this:
-h file True if file exists and is a symbolic link. -L file True if file exists and is a symbolic link.
Thanks, I already saw those but initially they did not work for directories. That was because I had
dirs=[^.]*/
that would expand, e.g:
foo/ bar/
and the trailing slash caused -L test to fail. So, finally, I have what I wanted without any external commands like this:
dirs=[^.]*/ for d in ${dirs} do if [[ -d ${d////} && ! -L ${d////} ]] then echo processing dir $d fi done # ... files=[^.]* for f in ${files} do if [[ -f ${f////} && ! -L ${f////} ]] then echo processing files $f fi done
Thanks!
____________________________________________________________________________________ Don't pick lemons. See all the new 2007 cars at Yahoo! Autos. http://autos.yahoo.com/new_cars.html
On 31Jan2007 08:47, Daniel Qarras dqarras@yahoo.com wrote: | > A POSIX shell requires that the test command supports this: | > | > -h file | > True if file exists and is a symbolic link. | > -L file | > True if file exists and is a symbolic link. | | Thanks, I already saw those but initially they did not work for | directories. That was because I had | | dirs=[^.]*/ | | that would expand, e.g: | | foo/ | bar/ | | and the trailing slash caused -L test to fail.
Indeed, because it forces a test to be _not_ on the symlink but on what is on the far side of it.
BTW, if you've been following this thread you should know that:
[^.]*/
is better written:
*/
which means _exactly_ the same thing.
Of course, you realise that you may not need to test for -d at all because */ will only return directories (or things pointing at them). (Except when there are no directories at all - the it returns "*/" unchanged and you just need to test for existence.)
Cheers,