link files within directory, with simple command similar to cp - linux

Where my question originated:
When running cp source/files.all destination/, all the files within source will now also exist in destination
Question:
What if I didn't want to duplicate the data from source into destination, but simply link them (with absolute path). Usually, I would run something like:
for f in $(ls source/); do ln -s $(pwd)/${f} $(pwd)/destination; done
Is there a simple command/tool that I can use (e.g. ln -a source/files.all destination/) which would create a softlink to all files in a directory, while automatically adding the absolute path as prefix. ln -r is close to what I need, but the absolute path, not the relative one?

I would use find "$PWD/source" -exec ln -s {} destination \;. The absolute path used as the first argument to find will cause {} to be replaced by an absolute path to the source file for each command.
GNU ln supports the -t option to specify the destination directory, allowing you to use a more efficient invocation of find:
find "$PWD/source" -exec ln -s -t destination {} +
The -exec ... + form requires {} to be the last argument in the command; -t lets you move the destination argument up to accommodate that requirement.

So I eventually sort of found a simple way to do this:
Simply run ln -s $(pwd -P)/source/* destination/

Related

Run script on every level of directory

I have a script called summarize.sh which produces a summary of the file/dirs inside of a directory. I would like to have it run recursively down the whole tree from the top. Whats a good way to do this?
I have tried to loop it with a for loop with
for dir in */; do
cd $dir
./summarize.sh
cd ..
however it returns ./summarize.sh: no file or directory
Is it because I am not moving the script as I run it? I am not very familiar with unix directories.
You can recursively list files using find . -type f and make your script take the interested file as a first argument, so you can do find . -type f -exec myScript.sh {} \;
If you want directories only, use find . -type d instead, or if you want both use just find . without restriction.
Additional option by name, e.g. find . -name '*.py'
Finally, if you do not want to recurse down the directory structure, i.e. only summarize the top level, you can use -maxdepth 1 option, so something like find . -type d -maxdepth 1 -exec myScript.sh {} \;.
The issue is that you are changing to a different directory with the cd command while your summarize.sh script is not located in these directories. One possible solution is to use an absolute path instead of a relative one. For example, change:
./summarize.sh
to something like:
/path/to/file/summarize.sh
Alternatively, under the given example code, you can also use a relative path pointing to the previous directory like this:
../summarize.sh
Try this code if you are running Bash 4.0 or later:
#! /bin/bash -p
shopt -s nullglob # Globs expand to nothing when they match nothing
shopt -s globstar # Enable ** to expand over the directory hierarchy
summarizer_path=$PWD/summarize.sh
for dir in **/ ; do
cd -- "$dir"
"$summarizer_path"
cd - >/dev/null
done
shopt -s nullglob avoids an error in case there are no directories under the current one.
The summarizer_path variable is set to an absolute path for the summarize.sh program. That is necessary to allow it to be run in directories other than the current one. (./summarize.sh only works in the current directory, ..)
Use cd -- ... to avoid problems if any directory name begins with '-'.
cd - >/dev/null to cd to the previous directory, and throw away its path when it is output by cd -.
Shellcheck issues several warnings about the code above, all to do with the use of cd. I'd fix them for "real" code.

Symlink multiple files to an existing folder

I have this command:
ln -sf src/* lang/golang/src/genericc/
I want to symlink all the files in src to the existing genericc directory, but when I run the above command I get broken symlinks in the destination. Anyone know how to do this?
Symlinks created with relative paths (i.e. where the source path doesn't start with "/") get resolved relative to the directory the link is in. That means a link to "src/foo.c" in the lang/golang/src/genericc/ directory would try to resolve to lang/golang/src/genericc/src/foo.c which probably doesn't exist.
Solution: either use an absolute path to the source files, like this:
ln -sf /path/to/src/* lang/golang/src/genericc/
or, to get the * wildcard to work right with a correct command, cd to the target directory so the relative paths will work the same way during creation that they will during resolution:
cd lang/golang/src/genericc
ln -sf ../../../../src/* ./
First of all, you can try ln -s $PATH_TO_SRC/* $PATH_TO_TARGET/.
However, it might have the "Argument list too long error".
Then you can use:
find $PATH_TO_SRC/ -type f -name "*.jpg" -exec cp -s {} . \;
Because if you use ln -s with find or bash loop, it will only create an empty link. Instead, we can use cp -sto create a smylink as well.
With the -r option, ln creates a link to the actual files wherever they are:
ln -srf src/* lang/golang/src/genericc/

Synchronize content of directories in Linux

Let's assume I have following source directory
source/
subdir1/file1
subdir1/file2
subdir2/file3
subdir3/file4
and target directory
target
subdir1/file5
subdir2/file6
subdir4/file7
I would like to move content of source subdirectories to right target subdirectories so result look like this
target
subdir1/file1
subdir1/file2
subdir1/file5
subdir2/file6
subdir2/file3
subdir3/file4
subdir4/file7
Is there some Linux command to do this or must I write a script myself?
To suimmarize, it is important to move, not copy. That rules out cp and rsync but allows mv. mv, however, has the issue that it is not good at merging the old directory into the new.
In the examples that you gave, the target directory had the complete directory tree but lacked files. If that is the case, try:
cd /source ; find . -type f -exec sh -c 'mv "$1" "/target/$1"' _ {} \;
The above starts by selecting the source as the current directory with cd /source. Next, we use find which is the usual *nix utility for finding files/directories. In this case, give find the -type f option to tell it to look only for files. With the -exec option, we tell it to move any such files found to the target directory.
You have choices for how to deal with conflicts between the two directory trees. You can give mv the -f option and it will overwrite files in the target without asking, or you can give it the -n option and it will never overwrite a target file, or your can give it the -i option and it will ask you each time.
In case the target directory tree is incomplete
If the target directory tree is missing some directories that are in the source, the we have to create them on the fly. This adds just minor complication:
cd /source ; find . -type f -exec sh -c 'mkdir -p "/target/${1%/*}"; mv "$1" "/target/$1"' _ {} \;
The mkdir -p command assures that the directory we want exists before we try to move the file there.
Additional notes
The form ${1%/*} is an example of one of the shells powerful features called "parameter expansion". This particular feature is suffix removal. In general, it looks like ${parameter%word} which tells bash to expand word and remove it from the end of parameter. In our case, the name of the parameter is 1, meaning the first argument to the script. We want to remove the file name and just leave behind the directory that the file is in. So, the word /* tells the shell to remove the last slash and any characters which follow.
The commands above use both single and double quotes. They have to be copied exactly for the command to work properly.
To sync dorectory maybe used rsync
Example:
rsync -avzh source/ target/
More info man rsync
Move (no copy)
rsync --remove-source-files -avzh source/ target/

Backup files with dir structure bash script

I'm making a bash script that should backup all files and dir structure to another dir.
I made the following code to do that:
find . -type f -exec cp {} $HOME/$bdir \; -o -type d -exec mkdir -p {} $HOME/$bdir \; ;
The problem is, is that this only copies the files and not the dir structure.
NOTE: I may not use cp -r, cp -R or something like it because this code is part of an assignment.
I hope somebody can put me in the right direction. ;)
Joeri
EDIT:
I changed it to:
find . -type d -exec mkdir -p $HOME/$bdir/{} \; ;
find . -type f -exec cp {} $HOME/$bdir/{} \; ;
And it works! Ty guys ;)
This sounds like a job for rsync.
You mention that this is an assignment. What are your restrictions? Are you limited to only using find? Does it have to be a single command?
One way to do this is to do it in two find calls. The first call only looks for directories. When a directory is found, mkdir the corresponding directory in the destination hierarchy. The second find call would look for files, and would use a cp command like you currently have.
You can also take each filename, transform the path manually, and use that with the cp command. Here's an example of how to generate the destination filename:
> find . -type f | sed -e "s|^\./|/new/dir/|"
/new/dir/file1.txt
/new/dir/file2.txt
/new/dir/dir1/file1_1.txt
/new/dir/dir1/file1_2.txt
For your purposes, you could write a short bash script that take the source file as input, uses sed to generate the destination filename, and then passes those two paths to cp. The dirname command will return the directory portion of a filename, so mkdir -p $(dirname $destination_path) will ensure that the destination directory exists before you call cp. Armed with a script like that, you can simply have find execute the script for every file it finds.
cd olddir; tar c . | (cd newdir; tar xp)
Can you do your find with "-type d" and exec a "mkdir -p" first, followed by your find that copies the files rather than having it all in one command? It should probably also be mkdir -p $HOME/$bdir/{}.

Copy all files in a directory to a local subdirectory in linux

I have a directory with the following structure:
file_1
file_2
dir_1
dir_2
# etc.
new_subdir
I'd like to make a copy of all the existing files and directories located in this directory in new_subdir. How can I accomplish this via the linux terminal?
This is an old question, but none of the answers seem to work (they cause the destination folder to be copied recursively into itself), so I figured I'd offer up some working examples:
Copy via find -exec:
find . ! -regex '.*/new_subdir' ! -regex '.' -exec cp -r '{}' new_subdir \;
This code uses regex to find all files and directories (in the current directory) which are not new_subdir and copies them into new_subdir. The ! -regex '.' bit is in there to keep the current directory itself from being included. Using find is the most powerful technique I know, but it's long-winded and a bit confusing at times.
Copy with extglob:
cp -r !(new_subdir) new_subdir
If you have extglob enabled for your bash terminal (which is probably the case), then you can use ! to copy all things in the current directory which are not new_subdir into new_subdir.
Copy without extglob:
mv * new_subdir ; cp -r new_subdir/* .
If you don't have extglob and find doesn't appeal to you and you really want to do something hacky, you can move all of the files into the subdirectory, then recursively copy them back to the original directory. Unlike cp which copies the destination folder into itself, mv just throws an error when it tries to move the destination folder inside of itself. (But it successfully moves every other file and folder.)
You mean like
cp -R * new_subdir
?
cp take -R as argument which means recursive (so, copy also directories), * means all files (and directories).
Although * includes new_subdir itself, but cp detects this case and ignores new_subdir (so it doesn't copy it into itself!)
Try something like:
cp -R * /path_to_new_dir/

Resources