Rename all subdirectories in csh shell - unix

I'm using csh and I have a directory structure containing multiple sub-directories. I'm trying to rename all the directories and sub-directories but not the files inside these directories. So something like
From
topdir1
--dir11
--dir12
topdir2
--dir21
----dir211
--dir22
to
topdir1.test
--dir11.test
--dir12.test
topdir2.test
--dir21.test
----dir211.test
--dir22.test
I can list the directories with find . -maxdepth 3 -type d. I'm trying to use a foreach loop to rename them. So
foreach i (`find . -maxdepth 3 -type d`)
mv $i $i.test
end
But this doesn't work as once the top level directory is renamed, it cannot find the sub-directories, so it only renames the top level directories.
Any idea on how to go about this?
Thanks

How about reversing the find results so that the subdirectories are listed first?
foreach i (`find ./* -maxdepth 3 -type d | sort -r`)
mv $i $i.test
end
Sort will output the longest directory names last, using the -r (reverse) flag changes it so that the lowest directories will be listed first, and be renamed before their parent directories do.

Use the -depth option to find.
From the solaris man find page:
-depth Always true. Causes descent of the
directory hierarchy to be done so that
all entries in a directory are acted on
before the directory itself. This can
be useful when find is used with cpio(1)
to transfer files that are contained in
directories without write permission.

Why use a loop? Just let find do the work:
find . -depth -maxdepth 3 -type d -exec mv {} {}.test \;
That is not strictly portable (some implementations of find may legally not expand {}.test to the string you want, so you might prefer:
find . -depth -maxdepth 3 -type d -exec sh -c 'mv $0 $0.test' {} \;

Related

List only folders which matches the wildcard expression

Is there a command which lists all folders that matches a wildcard expression? Example, if there are thousands of directories and I only want those ending in M or those starting in JO to be listed, can I do that with a certain Linux command? Thanks!
Use find command, for example:
# find anything that start with 'jo' end with 'm' (case insensitive)
find . -iname 'jo*m'
You can execute any command after that, for example:
# find just like above but case sensitive, and move them to `/tmp`
find . -name 'JO*M' -exec mv -v {} /tmp \;
To find only a directory, you can use -type d flag, for example:
# find any directory that start with JO
find . -name 'JO*' -type d
Explanation, first argument is the starting directory, . means current directory. The next argument means the search criteria -name for case sensitive search, -iname for case insensitive search, -type for type of item search, -exec to execute certain command where the {} is the file name matched. You can learn more here or for your specific case here.

Using find to delete all files with a given name except the one with a given extension

I want to delete all files in a directory with a given name, except for one with a given extension. I.e. we have a directory with:
foo.txt foo.exe foo.jpg foo.png foo.something foo.somethingelse bar.jpg bar.exe
I want to get rid of foo.txt foo.jpg foo.png foo.something foo.somethingelse
BUT crucially I don't want to get rid of foo.exe
Is there an easy one liner to do this?
Thank you
You can use ! inside a find command to exclude things, so something like:
find . -maxdepth 1 -type f -name "foo.*" ! -name foo.exe -exec rm '{}' \;
----------- ------- ------------- --------------- ----------------
in this dir files named foo.* but not foo.exe ...destroy them.
That should delete files matching foo.* in the current directory but leave foo.exe alone.

Find/grep statement code filter

I need to generate a list of IFS files that contain a given string
("iim"). (IFS is the IBM System i database) I need to search directory /linoma/goanywhere/projects
recursively. I've been able to do this with a combination of the FIND
and GREP commands in QSHELL:
find /linoma/goanywhere/userdata/projects -type f -exec grep -lRF "iim"
'{}' ';'
Here's the rub: there is a subdirectory I want to ignore
(/linoma/goanywhere/userdata/projects/demo). How would I modify my
find/grep statement to exclude the demo folder?
find /linoma/goanywhere/userdata/projects -( -type f -and -not -path '/linoma/goanywhere/userdata/projects/demo/**' -) -exec grep -IRF 'iim' '{}' ';'
should work for GNU find, I believe. If your local find doesn't support that syntax, you might also brute-force remove by appending | grep -v /linoma/goanywhere/userdata/projects/demo

Using mtime other than with FIND

I am trying to write a script which will move files older than 1 day to an archive directory. I used the following find command:
for filename in `find /file_path/*.* -type f -mtime +1`
This fails since my argument list is too big to be handled by find. I got the following error:
/usr/bin/find: arg list too long
Is it possible to use find in an IF-ELSE statement? Can someone provide some examples of using mtime other then in find.
Edit: To add the for loop of which the find is a part.
find /file_path -name '*.*' -mtime +1 -type f |
while read filename
do ...move operation...
done
That assumes your original code was acceptable in the way it handled spaces etc in file names,
and that there is no sensible way to do the move in the action of find. It also avoids problems with overlong argument lists.
Why not just use the -exec part of find?
If you just want to cp files, you could use
find /file_path -name "." -mtime +1 -type f | xargs -i mv {} /usr/local/archived

GNU find: Search in current directory first

how can I tell find to look in the current folder first and then continue search in subfolders? I have the following:
$ find . -iname '.note'
folder/1/.note
folder/2/.note
folder/3/.note
folder/.note
What I want is this:
$ find . -iname '.note'
folder/.note
folder/1/.note
folder/2/.note
folder/3/.note
Any ideas?
find's algorithm is as follows:
For each path given on the command line, let the current entry be that path, then:
Match the current entry against the expression.
If the current entry is a directory, then perform steps 1 and 2 for every entry in that directory, in an unspecified order.
With the -depth primary, steps 1 and 2 are executed in the opposite order.
What you're asking find to do is to consider files before directories in step 2. But find has no option to do that.
In your example, all names of matching files come before names of subdirectories in the same directory, so find . -iname '.note' | sort would work. But that obviously doesn't generalize well.
One way to process non-directories before directories is to run a find command to iterate over directories, and a separate command (possibly find again) to print matching files in that directory.
find -type d -exec print-matching-files-in {} \;
If you want to use a find expression to match files, here's a general structure for the second find command to iterate only over non-directories in the specified directory, non-recursively (GNU find required):
find -type d -exec find {} -maxdepth 1 \! -type d … \;
For example:
find -type d -exec find {} -maxdepth 1 \! -type d -iname '.note' \;
In zsh, you can write
print -l **/(#i).note(Od)
**/ recurses into subdirectories; (#i) (a globbing flag) interprets what follows as a case-insensitive pattern, and (Od) (a glob qualifier) orders the outcome of recursive traversals so that files in a directory are considered before subdirectories. With (Odon), the output is sorted lexicographically within the constraint laid out by Od (i.e. the primary sort criterion comes first).
Workaround would be find . -iname '.note' | sort -r:
$ find . -iname '.note' | sort -r
folder/.note
folder/3/.note
folder/2/.note
folder/1/.note
But here, the output is just sorted in reverse order and that does not change find's behaviour.
For me with GNU find on Linux I get both orderings with different test runs.
Testcase:
rm -rf /tmp/depthtest ; mkdir -p /tmp/depthtest ; cd /tmp/depthtest ; for dir in 1 2 3 . ; do mkdir -p $dir ; touch $dir/.note ; done ; find . -iname '.note'
With this test I get the poster's first result. Note the ordering of 1 2 3 .. If I alter this ordering to to . 1 2 3
rm -rf /tmp/depthtest ; mkdir -p /tmp/depthtest ; cd /tmp/depthtest ; for dir in . 1 2 3 ; do mkdir -p $dir ; touch $dir/.note ; done ; find . -iname '.note'
I get the poster's second result.
In either case adding -depth to find does nothing.
EDIT:
I wrote a perl oneliner to look in to this further:
perl -e 'opendir(DH,".") ; print join("\n", readdir(DH)),"\n" ; closedir(DH)'
And I ran this against /tmp/depthtest after running testcase 1 with these results:
.
..
1
2
3
.note
I ran it again after testcase 2 with these results:
.
..
.note
1
2
3
Which confirms that the results are in directory order.
The -depth option to find only controls whether e.g. ./1/.note is processed before or after ./1/, not whether ./.note or ./1/ is first, so the order of the results is purely based on directory order (which is mostly creation order).
It might be helpful to look at How do I recursively list all directories at a location, breadth-first? to learn how to work around this problem.
find -s . -iname ".note" doesn't help? or find . -iname '.note'|sort ?
Find in the current folder
find ./in_save/ -type f -maxdepth 1| more
==>73!

Resources