How to zip a directory without having to cd into the directory and still keeping the nested folder structure - zip

As part of a sh script I create a folder I would like to zip. Looking something like this:
my-directory-to-zip
├── file1
├── file2
├── dir1
│ ├── file3
│ ├── file4
When I then zip it the files are then nested inside my-directory-to-zip, where I sould like he folder structure to look something like this inside the zip file:
file1
file2
dir1
├── file3
├── file4
you can imagine my current sh-script looks something like this:
<commands that makes and fills my-directory-to-zip>
zip -r my-zip-file.zip ./my-directory-to-zip
rm -r my-directory-to-zip
I have tried:
<commands that makes and fills my-directory-to-zip>
cd my-directory-to-zip
zip -r ../my-zip-file.zip .
cd ..
rm -r my-directory-to-zip
but that fails with:
bash: cd: my-directory-to-zip: No such file or directory
When I copy pastes the commands directly into the terminal it works perfectly, with the cd into the directory.

Related

How to copy folders and subfolders which have selected files

I have a directory oridir with structure as follows:
.
├── DIRA
│   ├── DIRA1
│   │   └── file2.txt
│   └── DIRA2
│   ├── file1.xls
│   └── file1.txt
└── DIRB
├── DIRB1
│   └── file1.txt
└── DIRB2
└── file2.xls
I have to copy files which have extension .xls while maintaining the directory structure. So I need to have following directory and files in a newdir folder:
.
├── DIRA
│   └── DIRA2
│   └── file1.xls
└── DIRB
└── DIRB2
└── file2.xls
I tried following command but it copies all files and folders:
cp -r oridir newdir
Finding required files can be done as follows:
$ find oridir | grep xls$
oridir/DIRB/DIRB2/file2.xls
oridir/DIRA/DIRA2/file1.xls
Also as follows:
$ find oridir -type f -iname *.xls
./oridir/DIRB/DIRB2/file2.xls
./oridir/DIRA/DIRA2/file1.xls
But how to create these folders and copy files. How can I achieve this selected creation of directories and copying files with `bash' in Linux?
Edit: There are space also in some file and directory names.
cp's --parents flag makes use full source file name under DIRECTORY
For example, if recursive glob ** is enabled (shopt -s globstar):
cp --parents origin/**/*.xls target
If recursive glob is not enabled, you have to add a wildcard for each level on directory hierarchy:
cp --parents origin/*/*/*.xls target
If a destination dir is "dest".
foo.sh
#!/bin/bash
dest=./dest
find . -type f -name "*.xls" | while read f
do
d=$(dirname "${f}")
d="${dest}/${d}"
mkdir -p "${d}"
cp "${f}" "${d}"
done
Make dirs and files.
$ mkdir -p DIRA/DIRA1
$ mkdir -p DIRA/DIRA2
$ mkdir -p DIRB/DIRB1
$ mkdir -p DIRB/DIRB2
$ touch DIRA/DIRA1/file2.txt
$ touch DIRA/DIRA2/file1.xls
$ touch DIRA/DIRA2/file1.txt
$ touch DIRB/DIRB1/file1.txt
$ touch DIRB/DIRB1/file2.xls
A result is
$ find dest
dest
dest/DIRB
dest/DIRB/DIRB1
dest/DIRB/DIRB1/file2.xls
dest/DIRA
dest/DIRA/DIRA2
dest/DIRA/DIRA2/file1.xls
See Yuji's excellent answer first, but I think tar is also a good option here:
cd oridir; find . -name "*.xls" | xargs tar c | (cd ../newdir; tar x)
You may need to adjust oridir and/or ../newdir depending on the precise paths of your directories.
Possible improvement: Here is a version that may be better in that it will handle files (and paths) with spaces (or other strange characters) in their names, and that uses tar's own options instead of xargs and cd:
cd oridir; find . -print0 -name "*.xls" | tar -c --null -T- | tar -C ../newdir -x
Explanation:
The -print0 and the --null cause the respective commands to separate filenames by the null (ASCII 0) character only.
-T- causes tar to read filenames from standard input.
-C causes tar to cd before extracting.

How to run a command on file in directory recursively creating a new file in each directory being visited (Bash)?

I have a directory with a few sub-directories.
.
├── alembic
│   └── results.csv
├── bigdata
│   └── results.csv
├── calchipan
│   └── results.csv
I'd like to create a copy of each results.csv file in the same directory named results_cleaned.csv where certain lines will be removed. Each sub-directory is known to contain only a single file, results.csv.
Running this on a single directory works:
find . -name 'results.csv' | xargs grep -v "Pattern" > results_cleaned.csv
However, running the same command on a root, produces just a single results_cleaned.csv file. I understand that I have to specify that I want to create a file in each sub directory individually, how do I do this?
You can't use xarg since all values are feed to single grep.
It would be better to iterate over each line of find (file names):
find . -name 'results.csv' | while read -r f
do
grep -v "Pattern" "$f" > "$(dirname "$f")/results_cleaned.csv"
done

Having trouble arranging files into directories

I am attempting to organise text files of name f##.txt (#being a digit 0-9) into directories such that they end up as d#/f#.txt.
#! /bin/bash
for i in {0..9} ;
do
mkdir -p "$1/d$i "
for j in {0..9};
do
FILE= "/$1/f$i$j.txt"
if [ -f FILE ];
then
echo 'Moving!'
mv "/$1/f$i$j.txt" "/$1/d$i/f$j.txt"
fi
done
done
The code above is what I have so far but I keep getting the error:
CO1101/OSN2/q4-arrange.sh: line 8: /test/f00.txt: No such file or directory
I can't figure out where I'm going wrong.
The test directory is inside the current working directory
It looks like you have a space between FILE= and "/$1/f$i$j.txt". Try removing the space and see what happens. Bash does not like spaces in variable assignments (i.e. around the equal sign).
Hope this helps.
You could try something like this:
#!/usr/bin/env bash
pref=f ext=txt
for f in "$pref"[0-9][0-9]."$ext"; do
num=${f#"$pref"} num=${num%."$ext"}
dir=d${num%?} file=$pref${num#?}.$ext
[[ -d $dir ]] || mkdir -- "$dir" || exit 1
mv -- "$f" "$dir/$file"
done
Test run:
$ tree
.
├── f11.txt
├── f14.txt
├── f28.txt
├── f30.txt
└── script
$ ./script
$ tree
.
├── d1
│   ├── f1.txt
│   └── f4.txt
├── d2
│   └── f8.txt
├── d3
│   └── f0.txt
└── script
If you have the rename utility - also known as Perl rename, you can use this rather succinct command:
rename --dry-run -p 's|f([0-9])([0-9])|d$1/f$2|' *txt
Sample Output
'f11.txt' would be renamed to 'd1/f1.txt'
'f14.txt' would be renamed to 'd1/f4.txt'
'f28.txt' would be renamed to 'd2/f8.txt'
'f30.txt' would be renamed to 'd3/f0.txt'
See comments here on how the package may be called in various distros.
If anyone is on macOS, you can install it with homebrew using:
brew install rename

Linux command to move all the files from var/A to var/

I guess that this question has been asked here, but I didn't find it.
I am at directory var/ and I have a folder var/A that have some files inside, what I want is to move this files inside A to var/. so what i want to do is the following:
from:
var
├── A
| ├── file1.txt
| └── file2.txt
└── file3.txt
to:
var
├── file1.txt
├── file2.txt
└── file3.txt
In /var I have tryed the following commands
sudo mv A /
sudo mv A/ ./
sudo mv A/ .
And no one had worked. Thank you in advance
I have found the answer:
mv A/* .
I hope it could help another one
You have to specify the right path or match
if you want to move the entire files or directory you have to something
like this using * (wildcards)
user#akuma:~/var$ mv ./A/* ./
You can type mv --help in your terminal for more information to see more option like below
user#akuma:~/WORKSPACE/Trash$ mv --help
Usage: mv [OPTION]... [-T] SOURCE DEST
or: mv [OPTION]... SOURCE... DIRECTORY
or: mv [OPTION]... -t DIRECTORY SOURCE...
Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
Mandatory arguments to long options are mandatory for short options too.
--backup[=CONTROL] make a backup of each existing destination file
-b like --backup but does not accept an argument
-f, --force do not prompt before overwriting
-i, --interactive prompt before overwrite
-n, --no-clobber do not overwrite an existing file
If you specify more than one of -i, -f, -n, only the final one takes effect.
--strip-trailing-slashes remove any trailing slashes from each SOURCE
argument
-S, --suffix=SUFFIX override the usual backup suffix
-t, --target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY
-T, --no-target-directory treat DEST as a normal file
-u, --update move only when the SOURCE file is newer
than the destination file or when the
destination file is missing
-v, --verbose explain what is being done
-Z, --context set SELinux security context of destination
file to default type
--help display this help and exit
--version output version information and exit
The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
The version control method may be selected via the --backup option or through
the VERSION_CONTROL environment variable. Here are the values:
none, off never make backups (even if --backup is given)
numbered, t make numbered backups
existing, nil numbered if numbered backups exist, simple otherwise
simple, never always make simple backups
GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
Full documentation at: <http://www.gnu.org/software/coreutils/mv>
or available locally via: info '(coreutils) mv invocation'
I managed to replicate your situation.
.
└── var
├── A
│   ├── file1.txt
│   └── file2.txt
└── file3.txt
2 directories, 3 files
user#host:~/soQ$ mv var/A/*.txt var/ && rm -rf var/A/
user#host:~/soQ$ tree
.
└── var
├── file1.txt
├── file2.txt
└── file3.txt
Hope this helps!
If you want to do this from var just do mv A/*.txt . && rm -rf A/
Also, let's assume you have the following structure in your directory.
.
├── A
│   ├── file3.txt
│   └── folder1
│   ├── file1.txt
│   └── file2.txt
└── file4.txt
If you want to copy just the *.txt file in var you need to use find like this:
find ./A/* -type f -exec mv {} ${PWD} \; && rm A
This looks at all the files and folders and returns the path for each file (each .txt for example) and then executes mv on each line and moves the files into the current directory (${PWD}).

Copy directory structure ignoring some files

I have a directory structure
./
└── file1
├── config.xml
├── config.yml
└── file2
├── config.xml
├── config.yml
└── file3
├── config.xml
└── config.yml
What i want is to copy same directory structure and everything but
ignoring config.yml files in the new location Any Linux Command or
script Thanks in advance
As i understood you just want to replicate the folder structure,
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.
Take a look at this https://stackoverflow.com/a/4073992/6916391
You can do it in two steps by copying the whole structure and then removing the config.yml files, this is:
cp -R old_structure_parent_dir new_structure_parent_dir
find new_structure_parent_dir -name config.yml -exec rm -rf {} \;

Resources