How do I selectively create symbolic links to specific files in another directory in LINUX? - 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

Related

Using Bash, how do I feed a file list into a 'ln -s' without using 'find'?

I want to create symlinks to all files in 'myfiles' which are not already linked to and specify the destination folder for the just-created symlinks.
I am using the following cmd, successfully, to generate the list of existing links, which point to 'myfolder' :
find ~/my-existing-links/ -lname '*/myfiles/*' -printf "%f\n" > results.txt
And I'm using the following cmd to reverse match i.e. to list the files in myfiles which are not linked to:
ls ~/myfiles | grep -vf results.txt > results2.txt
So, results2.txt has a list of the files, each of which I now want to create a new symlink to.... in a folder called ~/newlinks .
I know it is possible to feed 'ln -s' a file list using the find / exec combination i.e.
find ~/myfiles/ -exec ln -s {} -t ~/newlinks \; -print
.... but that would be the unfiltered file list in myfiles. I want to use the filtered list.
Any ideas how I can do this? I'm going to be adding files to myfiles regularly and so will periodically visit the folder for the purpose of generating symlinks for all the new files so I can divi the links up logically(rather than change the original filename).
Try with xargs:
cat results2.txt | xargs -I{} ln -s {} ~/newlinks
You can use xargs to apply the links, so that your composite command might look like this:
find ~/myfiles/ | grep -vf results.txt | xargs make-my-links
and make-my-links would be a script something like this:
#!/bin/sh
for source in "$#"
do
ln -s "$source" -t ~/newlinks
done
The separate script and loop are used with xargs because it does not accept a command-template, but will (default) send as many of the inputs as it thinks will fit on a command-line.
So, you have 3 entities of type directory:
~/myfiles/: contains your files.
~/my-existing-links/: contains links to files from ~/myfiles/.
~/newlinks/: contains links to new files from ~/myfiles/
To me, the third entity is rather unnecessary. Why the new links aren't created directly in ~/my-existing-links/?
I would only use a script to update the list of links in ~/my-existing-links/, whenever new files are added in ~/myfiles/:
update_v1.sh
#!/bin/bash
for f in $(find ~/myfiles -type f); do
ln -sf "$f" "~/my-existing-links/$(basename $f)"
done
update_v2.sh
find ~/myfiles -type f -exec sh -c \
'for f; do ln -sf "$f" "~/my-existing-links/${f#*/}"; done' sh {} +
update_print.sh
#!/bin/bash
for f in $(find ~/myfiles -type f); do
if [[ ! -L "~/my-existing-links/${f#*/}" ]]; then
echo "Link not existing for $f"
fi
done
Thanks, Thomas and pasaba... I found a way to do it:
So I did the following from ~/newlinks :
while read line; do ln -s "$line" "${line##*/}" ; done < ~/myfiles/results2.txt
Thanks again for your time.

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 to ignore directories and certain patterns when supplying command-line arguments

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.

list all xml files within a directory and subdirectory

I want to list all .xml files in a dir and its subdir. I tried ls -LR but not able to filter out other files apart from .xml..
I want something like ls -LR | grep *.xml .
Thanks!
You can use find command:
find . -type f -name '*.xml'
Since you tagged the question with bash:
shopt -s extglob globstar
ls !(exclude.this.dir)/**/*.xml

Copy folder structure (without files) from one location to another

I want to create a clone of the structure of our multi-terabyte file server. I know that cp --parents can move a file and it's parent structure, but is there any way to copy the directory structure intact?
I want to copy to a linux system and our file server is CIFS mounted there.
You could do something like:
find . -type d > dirs.txt
to create the list of directories, then
xargs mkdir -p < dirs.txt
to create the directories on the destination.
cd /path/to/directories &&
find . -type d -exec mkdir -p -- /path/to/backup/{} \;
Here is a simple solution using rsync:
rsync -av -f"+ */" -f"- *" "$source" "$target"
one line
no problems with spaces
preserve permissions
I found this solution there
1 line solution:
find . -type d -exec mkdir -p /path/to/copy/directory/tree/{} \;
I dunno if you are looking for a solution on Linux. If so, you can try this:
$ mkdir destdir
$ cd sourcedir
$ find . -type d | cpio -pdvm destdir
This copy the directories and files attributes, but not the files data:
cp -R --attributes-only SOURCE DEST
Then you can delete the files attributes if you are not interested in them:
find DEST -type f -exec rm {} \;
This works:
find ./<SOURCE_DIR>/ -type d | sed 's/\.\/<SOURCE_DIR>//g' | xargs -I {} mkdir -p <DEST_DIR>"/{}"
Just replace SOURCE_DIR and DEST_DIR.
The following solution worked well for me in various environments:
sourceDir="some/directory"
targetDir="any/other/directory"
find "$sourceDir" -type d | sed -e "s?$sourceDir?$targetDir?" | xargs mkdir -p
This solves even the problem with whitespaces:
In the original/source dir:
find . -type d -exec echo "'{}'" \; > dirs2.txt
then recreate it in the newly created dir:
mkdir -p <../<SOURCEDIR>/dirs2.txt
Substitute target_dir and source_dir with the appropriate values:
cd target_dir && (cd source_dir; find . -type d ! -name .) | xargs -i mkdir -p "{}"
Tested on OSX+Ubuntu.
If you can get access from a Windows machine, you can use xcopy with /T and /E to copy just the folder structure (the /E includes empty folders)
http://ss64.com/nt/xcopy.html
[EDIT!]
This one uses rsync to recreate the directory structure but without the files.
http://psung.blogspot.com/2008/05/copying-directory-trees-with-rsync.html
Might actually be better :)
A python script from Sergiy Kolodyazhnyy
posted on Copy only folders not files?:
#!/usr/bin/env python
import os,sys
dirs=[ r for r,s,f in os.walk(".") if r != "."]
for i in dirs:
os.makedirs(os.path.join(sys.argv[1],i))
or from the shell:
python -c 'import os,sys;dirs=[ r for r,s,f in os.walk(".") if r != "."];[os.makedirs(os.path.join(sys.argv[1],i)) for i in dirs]' ~/new_destination
FYI:
Copy top level folder structure without copying files in linux
How do I copy a directory tree but not the files in Linux?
Another approach is use the tree which is pretty handy and navigating directory trees based on its strong options. There are options for directory only, exclude empty directories, exclude names with pattern, include only names with pattern, etc. Check out man tree
Advantage: you can edit or review the list, or if you do a lot of scripting and create a batch of empty directories frequently
Approach: create a list of directories using tree, use that list as an arguments input to mkdir
tree -dfi --noreport > some_dir_file.txt
-dfi lists only directories, prints full path for each name, makes tree not print the indentation lines,
--noreport Omits printing of the file and directory report at the end of the tree listing, just to make the output file not contain any fluff
Then go to the destination where you want the empty directories and execute
xargs mkdir < some_dir_file.txt
find source/ -type f | rsync -a --exclude-from - source/ target/
Copy dir only with associated permission and ownership
Simple way:
for i in `find . -type d`; do mkdir /home/exemplo/$i; done
cd oldlocation
find . -type d -print0 | xargs -0 -I{} mkdir -p newlocation/{}
You can also create top directories only:
cd oldlocation
find . -maxdepth 1 -type d -print0 | xargs -0 -I{} mkdir -p newlocation/{}
Here is a solution in php that:
copies the directories (not recursively, only one level)
preserves permissions
unlike the rsync solution, is fast even with directories containing thousands of files as it does not even go into the folders
has no problems with spaces
should be easy to read and adjust
Create a file like syncDirs.php with this content:
<?php
foreach (new DirectoryIterator($argv[1]) as $f) {
if($f->isDot() || !$f->isDir()) continue;
mkdir($argv[2].'/'.$f->getFilename(), $f->getPerms());
chown($argv[2].'/'.$f->getFilename(), $f->getOwner());
chgrp($argv[2].'/'.$f->getFilename(), $f->getGroup());
}
Run it as user that has enough rights:
sudo php syncDirs.php /var/source /var/destination

Resources