How to ignore directories and certain patterns when supplying command-line arguments - linux

I'm a newbie at Linux, and I am wondering if there's a 'one-liner' command that allows me to link everything in a directory to another directory, but ignoring subdirectories and certain wildcards from the source directory.
Let's be more specific...let's say I want to link everything in /foo to /bar/tmp as in...
ln -s /foo/* /bar/tmp/.
...but I want to:
ignore any subdirectories in /foo
ignore any files with the wildcard
runscript*
Any suggestions on how to do this?

You could use find like this
find /foo -maxdepth 1 -type f ! -name 'runscript*' -exec ln -s {} /bar/tmp/ \;

Something like
cd /bar/tmp
find /foo -maxdepth 1 -a -type f -a \! -name 'runscript*' |
while read file; do
ln -s "$file"
done
could do the trick.

Related

Using find to delete symbolic links except those which point to directories

At the moment I recursively remove all softlinks from my current working directory like this:
find . -type l -delete
But I don't want to remove symlinks pointing to a directory anymore.
Is there simple way to customize the find command or do I have to omit the -delete and script something to inspect every found softlink "myself" before removing?
As already suggested in the comments, you can use the test utility for this; but you don't need readlink because test -d always resolves symbolic links.
# replace -print with -exec rm {} +
find . -type l ! -exec test -d {} \; -print
It might be slow due to the overhead from spawning a new process for each symlink though. If that's a problem, you can incorporate a small shell script in your find command to process them in bulks.
find . -type l -exec sh -c '
for link; do
shift
if ! test -d "$link"; then
set "$#" "$link"
fi
done
# remove echo
echo rm "$#"' sh {} +
Or, if you have GNU find installed, you can utilize the -xtype primary.
# replace -print with -delete
find -type l ! -xtype d -print

Recursively find all directories that match a string, and symbolically link folder inside a new directory

I have a large collection of photos, sorted by folder. I'd like to write a script that searches through, finds matching folder names, and creates symbolic links to the folders that match inside a new folder.
Something like this:
find /mnt/librarypool/user1/originals -type d -iname "*bbq*" -exec sh -c 'ln -s "{}" "/mnt/mediapool/bbq$("sh basename {}")"' \;
(this works, but returns an error, "sh: cannot open basename: No such file or directory")
This will find any folder that matches "bbq" inside /mnt/librarypool/user1/originals and will create a link inside /mnt/mediapool/bbq/ to that folder.
Is there a better way? And how can I clear that error?
You may be able to use this find command:
find /mnt/librarypool/user1/originals -type d -iname "*bbq*" -exec bash -c \
'for d; do ln -s "$d" "/mnt/mediapool/bbq/${d##*/}"; done' _ {} +
+ after find will pass multiple argument of found directories
for d will loop through those entries
${d##*/} is equivalent of basename command as it strips all path info
On my system at least the ln command does create a link with the basename of its target when given a directory as where to put the link, so something like this should indeed work:
find /mnt/librarypool/user1/originals \
-type d -iname "*bbq*" \
-exec ln -s {} /mnt/mediapool/bbq/ \;

How to find all files in subdirectories that match pattern and replace pattern

I am attempting to move some video files of mine into new subdirectories while also renaming them on my Unraid system. The files all follow a similar naming convention:
featurette name-featurette.mkv
I would like to move these files from their current directory to a subdirectory and rename them like this:
featurettes/featurette name.mkv
I am able to create the directories and relocate the files using find and execdir:
find . -type f -name *-featurette.mkv -maxdepth 2 -execdir mkdir ./featurettes/ \;
find . -type f -name *-featurette.mkv -maxdepth 2 -execdir mv {} ./featurettes/ \;
I am struggling with the renaming piece. I've tried the rename command but am unable to get it to work within the featurettes directory, let alone from two directories above, which is where I'd like to execute the command. I've tried the following command within the featurettes directory:
rename \-featurette.mkv .mkv *
However I get the error:
invalid option -- 'f'
I thought by escaping the dash I could avoid that issue, but it doesn't appear to work. Any advice on how to remove this pattern from all files within subdirectories matching it would be very much appreciated.
From man rename you see this command gets options and 3 positional parameters:
SYNOPSIS
rename [options] expression replacement file...
So in your case the first parameter is being interpreted as an option. You may use this syntax:
rename -- '-featurette' '' *-featurette.mkv
to rename the files. -- indicates that any options are over and what follows are only positional parameters.
Totally, to copy the files with one mv process and rename them:
mkdir -p target/dir
find . -maxdepth 2 -type f -name "*-featurette.mkv" -exec mv -t target/dir {} +
cd target/dir && rename -- '-featurette' '' *-featurette.mkv
If you want to rename many files located into different subdirectories, you can use this syntax:
find . -name "*-featurette.mkv" -print0 | xargs -r0 rename -- '-featurette' ''
find . \
-maxdepth 2 \
-type f \
-name '*-featurette.mkv' \
-execdir sh -c '
echo mkdir -p ./featurettes/
echo mv -- "$#" ./featurettes/
' _ {} \+
Issue with your implementations I fixed or improved:
-maxdepth 2 must precede -type f
-name '*-featurette.mkv' must have the pattern quoted to prevent the shell to expand globb it.
-execdir is best used with an inline shell, so it can also process multiple arguments from the same directory
Also keep in mind that while running a command with -execdir, find will cd to that directory. It means that mv -- "$#" ./featurettes/' will move files into the ./featurettes/' directory relative to were -execdir has just cd.
Version which also rename files while moving:
( has no echo dry-run protection, so use only if you are sure it does what you want )
#!/usr/bin/env sh
find . \
-maxdepth 2 \
-depth \
-name '*-featurette.mkv' \
-type f \
-execdir sh -c '
mkdir -p featurettes
for arg
do
basename=${arg##*/}
mv -- "$basename" "./featurettes/${basename%-featurette.mkv}.mkv"
done
' _ {} +
You can use Bash's shell parameter expansion feature to get the part of the file name, for example:
$> filename=name-featurette.mkv
$> echo ${filename%-*} #To print first part before '-'
name
$> echo ${filename##*.} #To get the extension
mkv
$> echo ${filename#*-} #To print the part after '-' with extension
featurette.mkv
With this and slightly modifying your find command, you should be able to move+rename your files:
find . -type f -name '*-featurette.mkv' -maxdepth 2 -execdir sh -c 'f="{}"; mv -- "$f" ./featurettes/"${f%-*}.mkv"' \;
In fact you should be able to combine both the find command into one to create_dir, move and rename file.
find . -type f -name '*-featurette.mkv' -maxdepth 2 -execdir sh -c 'f="{}"; mkdir ./featurettes; mv -- "$f" ./featurettes/"${f%-*}.mkv"' \;

Copy all directories except one

I'm copying all subdirectories with its contents to my current directory as follows:
cp -r dirToCopy/* .
But in the folder dirToCopy, there is one subfolder called dirNotToCopy which should not be copied.
How can I filter that particular folder out of my expression?
Use extended globbing:
shopt -s extglob
cp -r dirToCopy/!(dirNotToCopy) .
Well if you want to do it in single line:
find /path_to/dirToCopy -mindepth 1 -type d ! -name dirNotToCopy -exec cp -r {} . \;
One more way of doing the same.
find /path_to/dirToCopy -maxdepth 1 -type d ! -name dirNotToCopy -exec cp -r {} . \;
Instead of using mindepth suggested in the other answer, we should use maxdepth.
(I can't comment or edit another answer since I do not have enough reputation yet)
Also note that this command only copies the subdirectories, not the files in the directory.

How do I selectively create symbolic links to specific files in another directory in LINUX?

I'm not exactly sure how to go about doing this, but I need to create symbolic links for certain files in one directory and place the symbolic links in another directory.
For instance, I want to link all files with the word "foo" in its name in the current directory bar1 that does not have the extension ".cc" and place the symbolic links in a directory bar2.
I was wondering if there was single line command that could accomplish this in LINUX bash.
Assuming you are in a directory that contains directories bar1 and bar2:
find bar1 -name '*foo*' -not -type d -not -name '*.cc' -exec ln -s $PWD/'{}' bar2/ \;
Try this:
cd bar1
find . -maxdepth 1 -name '*foo*' -not -name '*.cc' -exec echo ln -s $PWD/{} ../bar2 \;
Once you are satisfied with the dry run, remove echo from the command and run it for real.
This is easily handled with extended globbing:
shopt -s extglob
cd bar2
ln -s ../bar1/foo!(*.cc) .
If you really want it all on one line, just use the command separator:
shopt -s extglob; cd bar2; ln -s ../bar1/foo!(*.cc) .
The two examples are identical, but the first is much easier to read.
This technically doesn't count as a one line answer...but it can be pasted in a single instance and should do what you are looking for.
list=`ls | grep foo | grep -v .cc`;for file in $list;do ln $file /bar2/;done

Resources