How to ls --ignore on osx - macos

I'm trying to do the following on OSX:
ls -lR --ignore *.app
So that I can recursively search through all folders except for .app folders.
However it seems there is seems to be no --ignore or --hide options in Darwin.
Perhaps a script to recursively search one folder deep for a given set and I'm not sure I cant pipe ls -lR through anything because of the format of the output:
./ROOT/Applications/Some_app:
drwxr-xr-x 3 admin root 102 26 Jun 11:03 app-bundle.app #<- WANT THIS
drwxr-xr-x# 24 admin root 816 26 Jun 11:24 folder #<- WANT THIS
./ROOT/Applications/Some_app/app-bundle.app: #<- DON'T WANT
drwxr-xr-x 7 admin root 238 26 Jun 11:03 Contents #<- DON'T WANT
...

Use find:
find . -ls -name '*.app' -prune

In bash, you can use extended globbing to exclude a pattern.
shopt -s extglob # this must be on its own line
echo !(*.app) # match everything except for the given pattern
If you have bash version 4 or higher, you can use globstar to do this recursively.
shopt -s globstar
shopt -s extglob
echo **/!(*.app)

An alternative is to pipe to grep:
ls | grep -v

Related

How to avoid an error when listing nonexistent files?

I'm grouping files by dates in the filenames and processing them by groups.
for m in {01..12}; do
for d in {01..31}; do
f=`ls ./mydir/2018.${m}.${d}T*.jpg`
# process files
done
done
However, the code raises error if no files exist for some dates, e.g.,
ls: cannot access '2018.01.20T*.jpg': No such file or directory
How can I skip missing dates?
Enable nullglob so non-matching wildcards expand to nothing. Then you can skip parsing ls altogether and simply iterate over the matching files.
shopt -s nullglob
for m in {01..12}; do
for d in {01..31}; do
for f in ./mydir/2018.${m}.${d}T*.jpg; do
# process file
done
done
done
If you want all the file names at once, store them in an array. Arrays are better than a plain strings because they can handle file names with spaces and other special characters.
shopt -s nullglob
for m in {01..12}; do
for d in {01..31}; do
files=(./mydir/2018.${m}.${d}T*.jpg)
# process files
echo "processing ${files[#]}..."
done
done
What's the cleanest way to localise the shopt so as to restore nullglob to its original (unknown) value after this block?
Use a subshell: surround the block with parentheses. A subshell creates a child process which ensures changes don't leak into the parent.
(
shopt -s nullglob
...
)
It's polite to do this whenever you're changing shell options, and it's an elegant alternative to pushd+popd. Note that any variable assignments will be local to the subshell, so be careful there.
Here is another way, using find:
Assume the following dir:
$ ls -l mydir/
total 0
-rw-r--r-- 1 0 Jan 23 16:46 2018.01.20Thellowet.jpg
-rw-r--r-- 1 0 Jan 23 16:47 2018.04.24Thellowet.jpg
-rw-r--r-- 1 0 Jan 23 16:46 some_random_crap
-rw-r--r-- 1 0 Jan 23 16:46 wet
-rw-r--r-- 1 0 Jan 23 16:46 when
-rw-r--r-- 1 0 Jan 23 16:46 who
-rw-r--r-- 1 0 Jan 23 16:46 wtf
Using find:
find ./mydir/ -type f -regextype sed -regex ".*2018\.[0-9]\{,2\}\.[0-9]\{,2\}T.*\.jpg.*" -exec echo "---{}" \;
Gives (minor processing of data by appending --- to the file name):
---./mydir/2018.04.24Thellowet.jpg
---./mydir/2018.01.20Thellowet.jpg
NOTE: This will also return files that have 2018.00.xy or 2018.xy.00 where x and y can be any number between 0 and 9
Regex explained:
.* : any pattern
[0-9]{,2}: a 2 digit number
The \ are used to escape special characters.

concatenate grep output to an echo statement in UNIX

I am trying to output the number of directories in a given path on a SINGLE line. My desire is to output this:
X-many directories
Currently, with my bash sript, I get this:
X-many
directories
Here's my code:
ARGUMENT=$1
ls -l $ARGUMENT | egrep -c '^drwx'; echo -n "directories"
How can I fix my output? Thanks
I suggest
echo "$(ls -l "$ARGUMENT" | egrep -c '^drwx') directories"
This uses the shell's feature of final newline removal for command substitution.
Do not pipe to ls output and count directories as you can get wrong results if special characters have been used in file/directory names.
To count directories use:
shopt -s nullglob
arr=( "$ARGUMENT"/*/ )
echo "${#arr[#]} directories"
/ at the end of glob will make sure to match only directories in "$ARGUMENT" path.
shopt -s nullglob is to make sure to return empty results if glob pattern fails (no directory in given argument).
as alternative solution
$ bc <<< "$(find /etc -maxdepth 1 -type d | wc -l)-1"
116
another one
$ count=0; while read curr_line; do count=$((count+1)); done < <(ls -l ~/etc | grep ^d); echo ${count}
116
Would work correctly with spaces in the folder name
$ ls -la
total 20
drwxrwxr-x 5 alex alex 4096 Jun 30 18:40 .
drwxr-xr-x 11 alex alex 4096 Jun 30 16:41 ..
drwxrwxr-x 2 alex alex 4096 Jun 30 16:43 asdasd
drwxrwxr-x 2 alex alex 4096 Jun 30 16:43 dfgerte
drwxrwxr-x 2 alex alex 4096 Jun 30 16:43 somefoler with_space
$ count=0; while read curr_line; do count=$((count+1)); done < <(ls -l ./ | grep ^d); echo ${count}
3

Recursively touch files with file

I have a directory that contains sub-directories and other files and would like to update the date/timestamps recursively with the date/timestamp of another file/directory.
I'm aware that:
touch -r file directory
changes the date/timestamp for the file or directory with the others, but nothing within it. There's also the find version which is:
find . -exec touch -mt 201309300223.25 {} +\;
which would work fine if i could specify the actual file/directory and use anothers date/timestamp. Is there a simple way to do this? even better, is there a way to avoid changing/updating timestamps when doing a 'cp'?
even better, is there a way to avoid changing/updating timestamps when doing a 'cp'?
Yes, use cp with the -p option:
-p
same as --preserve=mode,ownership,timestamps
--preserve
preserve the specified attributes (default:
mode,ownership,timestamps), if possible additional attributes:
context, links, xattr, all
Example
$ ls -ltr
-rwxrwxr-x 1 me me 368 Apr 24 10:50 old_file
$ cp old_file not_maintains <----- does not preserve time
$ cp -p old_file do_maintains <----- does preserve time
$ ls -ltr
total 28
-rwxrwxr-x 1 me me 368 Apr 24 10:50 old_file
-rwxrwxr-x 1 me me 368 Apr 24 10:50 do_maintains <----- does preserve time
-rwxrwxr-x 1 me me 368 Sep 30 11:33 not_maintains <----- does not preserve time
To recursively touch files on a directory based on the symmetric file on another path, you can try something like the following:
find /your/path/ -exec touch -r $(echo {} | sed "s#/your/path#/your/original/path#g") {} \;
It is not working for me, but I guess it is a matter of try/test a little bit more.
In addition to 'cp -p', you can (re)create an old timestamp using 'touch -t'. See the man page of 'touch' for more details.
touch -t 200510071138 old_file.dat

Bash Bug? Can't use negate extglob !(*/) to filter out directories?

If the glob */ only matches directories, then logically the extglob !(*/) should match non-directories; but this doesn't work. Is this a bug or am I missing something? Does this work on any shell?
Test 1 to prove that */ works
$ cd /tmp; ls -ld */
drwxr-xr-x 2 seand users 4096 Jan 1 15:59 test1//
drwxr-xr-x 2 seand users 4096 Jan 1 15:59 test2//
drwxr-xr-x 2 seand users 4096 Jan 1 15:59 test3//
Test 2 to show potential bug with !(*/)
$ cd /tmp; shopt -s extglob; ls -ld !(*/)
/bin/ls: cannot access !(*/): No such file or directory
In Bash, !() (like *, ?, *(), and #()) only applies to one path component. Thus, !(anything containing a / slash) doesn't work.
If you switch to zsh, you can use *(^/) to match all non-directories, or *(.) to match all plain files.
The answer to the specific question has already been given; and I am not sure if you really wanted another solution or if you were just interested to analyze the behavior, but one way to list all non-directories in the current folder is to use find:
find . ! -type d -maxdepth 1

Ruby one-line failing because of directory

I have a ruby one-liner ruby1.9 -ine '#some statement' src/**. I assumed, like perl does, ruby skips the directories ( well that's how I remember it ). But I get this error e:1:in 'gets': Is a directory. Besides giving it a list of files, is there a quick way of getting round this?
I don't think it ever skipped directories, at least even 1.8.6 does not. So I suppose the only quick way is to give it a list of files, or to manipulate ARGV, but it'd hardly be a proper one-liner then anymore.
Something like this:
ruby -ne 'ARGV.delete_if{|s| File.ftype(s) == "directory"}; do_stuff_here' src/**
So yeah, giving it a proper file list seems to be the nicer solution.
If you want only files recursively, then find(1) will be your best bet :
find ./src -type f | ruby1.9 -ne '#some statement'
I believe your assumption is actually about your shell's handling of **. The shell interprets un-escaped meta-characters in commands.
bash(1) by default will not expand ** recursively. You need to set the globstar option for this behavior:
$ ls -l /tmp
total 20
drwx------ 2 sarnold sarnold 4096 2011-11-17 15:43 keyring-9mdW7p
drwx------ 2 gdm gdm 4096 2011-11-17 15:43 orbit-gdm
drwx------ 2 sarnold sarnold 4096 2011-11-17 15:44 orbit-sarnold
drwx------ 2 sarnold sarnold 4096 2011-11-17 15:46 plugtmp
drwx------ 2 sarnold sarnold 4096 2011-11-17 15:43 ssh-ZriaCoWL2248
$ shopt -u globstar
$ echo /tmp/**
/tmp/keyring-9mdW7p /tmp/orbit-gdm /tmp/orbit-sarnold /tmp/plugtmp /tmp/ssh-ZriaCoWL2248
$ shopt -s globstar
$ echo /tmp/**
/tmp/ /tmp/keyring-9mdW7p /tmp/keyring-9mdW7p/control /tmp/orbit-gdm /tmp/orbit-sarnold /tmp/orbit-sarnold/linc-9a5-0-240e051029b41 /tmp/orbit-sarnold/linc-9ad-0-1b1412421b16c /tmp/plugtmp /tmp/ssh-ZriaCoWL2248 /tmp/ssh-ZriaCoWL2248/agent.2248

Resources