How to `ls` only one level deep? - bash

I have lots subdirectories containing data, and I want a short list of which jobs (subdirectories) I have. I'm not happy with the following command.
$ ls H2*
H2a:
energy.dat overlap.dat
norm.dat zdip.dat ...
(much more)
H2b:
energy.dat overlap.dat
norm.dat zdip.dat ...
(much more)
This needless clutter defeats the purpose of the wildcard (limiting the output). How can I limit the output to one level deep? I'd like to see the following output
H2a/ H2b/ H2z/
Thanks for your help,
Nick

Try this
ls -d H2*/
The -d option is supposed to list "directories only", but by itself just lists
.
which I personally find kind of strange. The wildcard is needed to get an actual list of directories.
UPDATE: As #Philipp points out, you can do this even more concisely and without leaving bash by saying
echo H2*/
The difference is that ls will print the items on separate lines, which is often useful for piping to other functions.

You should consider using find, like this:
find . -maxdepth 1 -type d -name "H2*"
NOTE: Putting "-type d" before "-maxdepth 1" results in a warning on Debian Linux ("find: warning: you have specified the global option -maxdepth after the argument -type, but global options are not positional, i.e., -maxdepth affects tests specified before it as well as those specified after it. Please specify global options before other arguments.") No such warning is issued on Mac.

echo H2*
It's Bash who does the expansion, so you don't even need ls.
Should you have both files and directories starting with H2, you can append a slash to restrict the glob to directories:
echo H2*/

Perhaps this is what you are looking for?
ls | grep H2*

Use tree by Steve Baker at http://mama.indstate.edu/users/ice/tree/
It fills in for a lot of things that are missing from ls.
To list directories one layer deep:
tree -adi -L 1 H2*

Related

How to use 'find' to print only matching directories?

I am trying to use find to recursively search the file names in a directory for a particular pattern (wp-config.*). When I do so using:
find `wp-config.*`
it seems to print all directories to the screen. How can I ensure that only the matching directories are printed to the screen?
Thanks.
From the answers in this outside post, I was able to use this command to do what I want:
find . -name 'wp-config.*' -printf "%h\n"
One of my main issues was that I originally did not understand that find does not print results to the screen. So it is necessary to pipe results to some kind of output.
Correct usage of find:
find -type d -name "wp-config.*"
The '-type d' will give you the directories.
The '-name "wp-config.*"' will give you the names requested.
Always use the man pages to search up commands. In this case:
man find
One last thing. The backticks ` are serving a totally different purpose. What you need here are regular quotes ".

Exactly why does result=$($shell_command) fail?

Why does assigning command output work in some cases and seemingly not in others? I created a minimal script to show what I mean, and I run it in a directory with one other file in it, a.txt. Please see the ??? in the script below and let me know what's wrong, perhaps try it. Thanks.
#!/bin/bash
## setup so anyone can copy/paste/run this script ("complete" part of MCVE)
tempdir=$(mktemp -d "${TMPDIR:-/tmp}"/demo.XXXX) || exit # make a temporary directory
trap 'rm -rf "$tempdir"' 0 # delete temporary directory on exit
cd "$tempdir" || exit # don't risk changing non-temporary directories
touch a.txt # create a sample file
cmd1="find . -name 'a*' -print"
eval $cmd1 # this produces "./a.txt" as expected
res1=$($cmd1)
echo "res1=$res1" # ??? THIS PRODUCES ONLY "res1=" , $res1 is blank ???
# let's try this as a comparison
cmd2="ls a*"
res2=$($cmd2)
echo "res2=$res2" # this produces "res2=a.txt"
Let's look at exactly what this does:
cmd1="find . -name 'a*' -print"
res1=$($cmd1)
echo "res1=$res1" # ??? THIS PRODUCES ONLY "res1=" , $res1 is blank ???
As per BashFAQ #50, execution of res1=$($cmd1) does the following, assuming you have no files with names starting with 'a and ending with ' (yes, with single quotes as part of the name), and that you haven't enabled the nullglob shell option:
res1=$( find . -name "'a*'" -print )
Note the quoting around than name? That quoting represents that the 's are treated as data, rather than syntax; thus, rather than having any effect on whether the * is expanded, they're simply an additional element required to be part any filename for it to match, which is why you get a result with no matches at all. Instead, as the FAQ tells you, use a function:
cmd1() {
find . -name 'a*' -print
}
res1=$(cmd1)
...or an array:
cmd1=( find . -name 'a*' -print )
res1=$( "${cmd1[#]}" )
Now, why does this happen? Read the FAQ for a full explanation. In short: Parameter expansion happens after syntactic quotes have already been applied. This is actually a Very Good Thing from a security perspective -- if all expansions recursively ran through full parsing, it would be impossible to write secure code in bash handling hostile data.
Now, if you don't care about security, and you also don't care about best practices, and you also don't care about being able to correctly interpret results with unusual filenames:
cmd1="find . -name 'a*' -print"
res1=$(eval "$cmd1") # Force parsing process to restart from beginning. DANGEROUS if cmd1
# is not static (ie. constructed with user input or filenames);
# prone to being used for shell injection attacks.
echo "res1=$res1"
...but don't do that. (One can get away with sloppy practices only until one can't, and the point when one can't can be unpleasant; for the sysadmin staff at one of my former jobs, that point came when a backup-maintenance script deleted several TB worth of billing data because a buffer overflow had placed random garbage in the name of a file that was due to be deleted). Read the FAQ, follow the practices it contains.

Python: Search for String in Filenames

I have been trying to search the root file system for a certain string (in the middle of the filename or wherever). I read about grep and I have tried this code here:
grep -rnw /home/pi/music -e "Maroon"
and something strange happens, there are three filenames with Maroon in them (same capitalization and spacing), but only two show up in the terminal. Any ideas why that is? Are there any other, easier ways to do this?
I would also like to say that I saw this StackOverflow post here, but I could not get it to work. I believe that was focusing on specific filenames, while I would like to do a general search.
All help is very much appreciated!
grep reads through the files on your disk, and searches for the word "Maroon".
What I think you want (when searching for file names) is:
find /home/pi/music -iname "*maroon*"
This will display all files that are named *maroon* (case insensitive). If you want case sensitive, take a look at -name.
man find
Will list all options for find.
The correct (or rather, the more common) way to search for files in matching a certain pattern is to use the find command, like this:
find /home/pi/music -type f -iname "*maroon*" -ls
type, limit searches to a particular type, in this case f for regular files (so it will ignore directories, pipes, sockets, etc.)
iname case insensitive name search
ls list the files found.
grep is used to search within files for matching content.
You want to use find to search for filenames
find /home/pi/music -depth 1 -name \*Maroon\*
This will find a file where the name contains the string. You need to quote the filename, so the shell doesn't glob it. -depth 1 so you only search the current directory

Bash scripting print list of files

Its my first time to use BASH scripting and been looking to some tutorials but cant figure out some codes. I just want to list all the files in a folder, but i cant do it.
Heres my code so far.
#!/bin/bash
# My first script
echo "Printing files..."
FILES="/Bash/sample/*"
for f in $FILES
do
echo "this is $f"
done
and here is my output..
Printing files...
this is /Bash/sample/*
What is wrong with my code?
You misunderstood what bash means by the word "in". The statement for f in $FILES simply iterates over (space-delimited) words in the string $FILES, whose value is "/Bash/sample" (one word). You seemingly want the files that are "in" the named directory, a spatial metaphor that bash's syntax doesn't assume, so you would have to explicitly tell it to list the files.
for f in `ls $FILES` # illustrates the problem - but don't actually do this (see below)
...
might do it. This converts the output of the ls command into a string, "in" which there will be one word per file.
NB: this example is to help understand what "in" means but is not a good general solution. It will run into trouble as soon as one of the files has a space in its nameā€”such files will contribute two or more words to the list, each of which taken alone may not be a valid filename. This highlights (a) that you should always take extra steps to program around the whitespace problem in bash and similar shells, and (b) that you should avoid spaces in your own file and directory names, because you'll come across plenty of otherwise useful third-party scripts and utilities that have not made the effort to comply with (a). Unfortunately, proper compliance can often lead to quite obfuscated syntax in bash.
I think problem in path "/Bash/sample/*".
U need change this location to absolute, for example:
/home/username/Bash/sample/*
Or use relative path, for example:
~/Bash/sample/*
On most systems this is fully equivalent for:
/home/username/Bash/sample/*
Where username is your current username, use whoami to see your current username.
Best place for learning Bash: http://www.tldp.org/LDP/abs/html/index.html
This should work:
echo "Printing files..."
FILES=(/Bash/sample/*) # create an array.
# Works with filenames containing spaces.
# String variable does not work for that case.
for f in "${FILES[#]}" # iterate over the array.
do
echo "this is $f"
done
& you should not parse ls output.
Take a list of your files)
If you want to take list of your files and see them:
ls ###Takes list###
ls -sh ###Takes list + File size###
...
If you want to send list of files to a file to read and check them later:
ls > FileName.Format ###Takes list and sends them to a file###
ls > FileName.Format ###Takes list with file size and sends them to a file###

Recursive grep for bash in cygwin

if i want an alias to do "rgrep pattern *" to search all files from my current location down through any sub directories, what is an alias for rgrep I can add to my bashrc file?
i would also like it to ignore errors and only report positive hits
In order for it to ignore errors (such as "Permission denied"), you'll probably need to use a function instead of an alias:
rgrep () { grep -r "${#}" 2>/dev/null; }
How about:
alias rgrep="grep -r"
This will only show 'positive hits', i.e. lines that contain the pattern you specify.
Small piece of advice, however: you might want to get used to just using grep -r directly. You'll then find it much easier if you ever need to use someone else's workstation, for instance!
Edit: you want to match patterns in file names, not in their contents (and also in directory names too). So how about this instead:
alias rgrep="find | grep"
By default, find will find all files and directories, so then it's just a case of passing that list to grep to find the pattern you're looking for.

Resources