Script to move files from sub directories to root folder [closed] - linux

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 months ago.
Improve this question
I have the following directory and sub directories structure:
eva tree
.
├── 1061
│   └── 2022
│   └── 09
│   └── 21
│   └── a0e51f58-5057-4002-b4c4-d3fb870e9b3a.json
├── 1769
│   └── 2022
│   └── 08
│   └── 30
│   └── e36d8e21-5184-489f-89b5-eb1fd5eba5f6.json
├── 1991
│   └── 2022
│   └── 09
│   └── 16
│   └── 1d0a4162-7e66-44c8-8b61-f3bc5dbdb107.json
I need to move all .json files to root folder eva.
Expected output:
.
│a0e51f58-5057-4002-b4c4-d3fb870e9b3a.json
│e36d8e21-5184-489f-89b5-eb1fd5eba5f6.json
│1d0a4162-7e66-44c8-8b61-f3bc5dbdb107.json
How can I do it using bash?

You can use find . -type f -name '*.json' -exec mv {} . \;
find . searches across the current directory;
-type f all objects of type f (aka file);
-name '*.json' file names that ends with the .json;
-exec [COMMAND]\; for each file found, run [COMMAND];
mv {} ., the curly brackets {} represent the found file, the command mves it to the current directory (.)

Related

does exec mv command used with find deletes the files?

I want to rename a few files in a folder with one command rather than rename them one by one.
I have a folder named /u0x/XMLs where there are around 500 folders. Each of these folders has a file named PROCESSED_ADI.XML. I want to rename all these files to ADI.XML.
I tried to use find and -exec mv together:
XMLs]$ find . -name "*.XML" -exec mv {} ADI.XML \;
After the command was executed all the files have been deleted from the folders or moved from there, which I am not sure. Could someone shed some light on what went wrong and is there any way the files could be retrieved?
After the command was executed all the files have been deleted from the folders or moved from there, which I am not sure
You're renaming the files but the destination or rather the renamed file has a different location, It is the current location/path where you ran the find command.
You can use -execdir if your find supports it.
find . -name "*.XML" -execdir mv -v {} ADI.XML \;
Here is definition of execdir
-execdir command {} +
Like -exec, but the specified command is run from the subdirectory containing the matched file, which is not normally the directory in which you started find. As with
-exec, the {} should be quoted if find is being invoked from a shell. This a much more secure method for invoking commands, as it avoids race conditions during resolu-
tion of the paths to the matched files. As with the -exec action, the `+' form of -execdir will build a command line to process more than one matched file, but any given
invocation of command will only list files that exist in the same subdirectory. If you use this option, you must ensure that your $PATH environment variable does not
reference `.'; otherwise, an attacker can run any commands they like by leaving an appropriately-named file in a directory in which you will run -execdir. The same
applies to having entries in $PATH which are empty or which are not absolute directory names. If any invocation with the `+' form returns a non-zero value as exit sta-
tus, then find returns a non-zero exit status. If find encounters an error, this can sometimes cause an immediate exit, so some pending commands may not be run at all.
The result of the action depends on whether the + or the ; variant is being used; -execdir command {} + always returns true, while -execdir command {} ; returns true only
if command returns 0.
Here is a bit of demo about what/why did your files got removed deleted.
Let's create a dummy directories and file anywhere but here we will create them in /tmp
cd /tmp
Let's create the directories and files.
mkdir -p u0x/XMLs/folder_{1..10} && touch u0x/XMLs/folder_{1..10}/PROCESSED_ADI.XML
Now check what was created inside those directories, using the tree command.
tree u0x/
Output
u0x/
└── XMLs
├── folder_1
│   └── PROCESSED_ADI.XML
├── folder_10
│   └── PROCESSED_ADI.XML
├── folder_2
│   └── PROCESSED_ADI.XML
├── folder_3
│   └── PROCESSED_ADI.XML
├── folder_4
│   └── PROCESSED_ADI.XML
├── folder_5
│   └── PROCESSED_ADI.XML
├── folder_6
│   └── PROCESSED_ADI.XML
├── folder_7
│   └── PROCESSED_ADI.XML
├── folder_8
│   └── PROCESSED_ADI.XML
└── folder_9
└── PROCESSED_ADI.XML
11 directories, 10 files
Now we execute your find command, but with the -v flag/option.
find u0x/ -name "*.XML" -exec mv -v {} ADI.XML \;
Output
renamed './XMLs/folder_2/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_9/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_7/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_10/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_6/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_3/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_8/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_1/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_4/PROCESSED_ADI.XML' -> 'ADI.XML'
renamed './XMLs/folder_5/PROCESSED_ADI.XML' -> 'ADI.XML'
Now check what happened to the files using the tree command.
tree u0x/
Output
u0x/
├── ADI.XML
└── XMLs
├── folder_1
├── folder_10
├── folder_2
├── folder_3
├── folder_4
├── folder_5
├── folder_6
├── folder_7
├── folder_8
└── folder_9
11 directories, 1 file
Now If you take a good look at the above output from tree all of the PROCESSED_ADI.XML are gone from it's location/folder but there is one ADI.XML inside the parent directory/folder u0x
Like what was mentioned by #Gordon Davidson All the xml (as long as the file ends in .xml) files has been moved in one location and it was overwritten again and again so now you have only one file named. ADI.XML
Using -execdir will have the expected output you're looking for.
The output of the tree command if -execdir was used, should be:
tree u0x/
u0x/
└── XMLs
├── folder_1
│   └── ADI.XML
├── folder_10
│   └── ADI.XML
├── folder_2
│   └── ADI.XML
├── folder_3
│   └── ADI.XML
├── folder_4
│   └── ADI.XML
├── folder_5
│   └── ADI.XML
├── folder_6
│   └── ADI.XML
├── folder_7
│   └── ADI.XML
├── folder_8
│   └── ADI.XML
└── folder_9
└── ADI.XML
11 directories, 10 files
First of all, do not use a wildcard. Even with -execdir, you're creating a circumstance where all .XML files in a directory will be overwritten with the last match in that directory.
find . -type f -name 'PROCESSED_ADI.XML' -exec sh -c 'for i do echo mv "${i}" "$(dirname "$i")/ADI.XML"; done' _ {} +
Run this, as a dry run. If it looks ok, remove echo.

Is it possible to compress files without keeping the structure in Linux? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 1 year ago.
Improve this question
I have the following directory structure:
A0
├── A1
│   ├── A1_B1
│   │   ├── A1_B1_1.docx
│   │   ├── A1_B2_2.pptx
│   ├── A1_B2
│   │   └── A1_B2_C1
│   │   ├── A1_B2_C1_D1
│   │   │   ├── A1_B2_C1_D1_1.docx
│   │   │   └── A1_B2_C1_D1_2.docx
│   │   └── A1_B2_C1.xlsx
├── A2
└── A0.txt
I want to create a .7z file that will contain only the files. I don't want to keep the folders. I have tried this answer and this answer but they don't work in Linux.
Is it possible to do it with 7z or I should extract files to a single directory first and then compress.
If for some reason the answers you reference to don't work try this instead.
Create a directory
mkdir flat_dir
Link all files from the desired folder recursively in flat_dir, for me the desired folder was cpptest.
for full_path_file in $(find ../cpptest -type f)
do
echo "$full_path_file"
filename=$(echo "$full_path_file" | rev | cut -d '/' -f 1 | rev)
echo "$filename"
ln -s -T "$full_path_file" "$filename"
done
Zip the files
7z a test.zip -l ~/flat_dir

How to extract all .tgz files in subdirectories? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 3 years ago.
Improve this question
I have a list of *tgz files in a ./main/ directory such as
$ tree -L 3
.
├── 191009-Grace_587_8G_R2
│   └── Grace_587_8G_R2
│   └── output.tgz
├── 191009-Grace_V0G_R2
│   └── Grace_V0G_R2
│   └── output.tgz
├── 191009-Grace_V8G_R2
│   └── Grace_V8G_R2
│   └── output.tgz
├── 191014-Grace_587_0G_R2
│   └── Grace_587_0G_R2
│   └── output.tgz
├── 191014-Grace_587_8G_R2
│   └── Grace_587_8G_R2
│   └── output.tgz
├── 191014-Grace_V0G_R2
│   └── Grace_V0G_R2
│   └── output.tgz
└── 191014-Grace_V8G_R2
└── Grace_V8G_R2
└── output.tgz
I am wondering how to extract them all together to the directory containing them.
Using tar's -C / --directory option:
-C, --directory=DIR
Change to DIR before performing any operations. This option is order-sensitive, i.e. it affects all options that follow.
for i in *-Grace_*/Grace_*/output.tgz; do
tar xzf "$i" --directory="${i%/*}"
done
The parameter expansion ${i%/*} removes the filename from the path (like the dirname command). To extract the files to the main directory, remove the --directory option.
Using find with the -execdir option:
find . -type f -name 'output.tgz' -execdir tar xfz {} +

bash script to rename following a pattern in subdirectories and make a copy

I am trying to do an iterative renaming of certain files in all directories.
homefolder/folder1/ouput/XXXXX_ab.png
homefolder/folder1/ouput/XXXXX_abcdefg.png
homefolder/folder2/ouput/XXXXX_ab.png
homefolder/folder2/ouput/XXXXX_abcdefg.png
homefolder/folder3/ouput/XXXXX_ab.png
homefolder/folder3/ouput/XXXXX_abcdefg.png
...
homefolder/folder500/ouput/XXXXX_ab.png
homefolder/folder500/ouput/XXXXX_abcdefg.png
I want to get the folder name (ex. folder1, folder2, ... folder500) and pass it to the two png files as a prefix and remove those five Xs at the beginning of each file.
The pattern of those png files are:
XXXXX_ab.png
XXXXX_abcdrfg.png
so only the first five characters are different in each subdirectory, which will be replaced by the name of its parent directory, those folder names.
the results will be:
homefolder/folder1/ouput/folder1_ab.png
homefolder/folder1/ouput/folder1_abcdefg.png
homefolder/folder2/ouput/folder2_ab.png
homefolder/folder2/ouput/folder2_abcdefg.png
homefolder/folder3/ouput/folder3_ab.png
homefolder/folder3/ouput/folder3_abcdefg.png
...
homefolder/folder500/ouput/folder500_ab.png
homefolder/folder500/ouput/folder500_abcdefg.png
at the end of renaming, create a copy of these two newly renamed files inside another folder in the homefolder, for example all_png_folder.
find . -iname "*_ab.png" -exec rename _ab.png folder1_ab.png '{}' \;
find . -name "*_ab.png" -exec cp {} ./all_png_folder \;
Here is a start, the copying at the end should be a trivial addition.
#!/usr/bin/env bash
files=$(find . -type f -name "*_ab.png" -or -name "*_abcdefg.png")
for file in $files; do
foldername=$(cut -d '/' -f 2 <<< $file)
# The name of the png-file minus the leading xxxxxx
pngfile=$(basename "$file" | cut -d '_' -f 2)
destinationdir=$(dirname "$file")
mv $file "$destinationdir/$foldername"'_'"$pngfile"
done
Demo
$ tree
.
├── folder1
│   └── ouput
│   ├── foo_bar.png
│   ├── xxxxx_abcdefg.png
│   └── xxxxx_ab.png
├── folder2
│   └── ouput
│   ├── xxxxx_abcdefg.png
│   └── xxxxx_ab.png
└── rename.sh
4 directories, 6 files
$ ./rename.sh
$ tree
.
├── folder1
│   └── ouput
│   ├── folder1_abcdefg.png
│   ├── folder1_ab.png
│   └── foo_bar.png
├── folder2
│   └── ouput
│   ├── folder2_abcdefg.png
│   └── folder2_ab.png
└── rename.sh

Linux/shell - Remove all (sub)subfolders from a directory except one

I've inherited a structure like the below, a result of years of spaghetti code...
gallery
├── 1
│   ├── deleteme1
│   ├── deleteme2
│   ├── deleteme3
│   └── full
│   ├── file1
│   ├── file2
│   └── file3
├── 2
│   ├── deleteme1
│   ├── deleteme2
│   ├── deleteme3
│   └── full
│   ├── file1
│   ├── file2
│   └── file3
└── 3
├── deleteme1
├── deleteme2
├── deleteme3
└── full
├── file1
├── file2
└── file3
In reality, this folder is thousands of subfolders large. I only need to keep ./gallery/{number}/full/* (i.e. the full folder and all files within, from each numbered directory within gallery), with everything else no longer required and needs to be deleted.
Is it possible to construct a one-liner to handle this? I've experimented with find/maxdepth/prune could not find an arragement which met my needs.
(Update: To clarify, all folders contain files - none are empty)
Using PaddyD answer you can first clean unwanted directories and then delete them:
find . -type f -not -path "./gallery/*/full/*" -exec rm {} + && find . -type d -empty -delete
This can easily be done with bash extglobs, which allow matching all files that don't match a pattern:
shopt -s extglob
rm -ri ./gallery/*/!(full)
How about:
find . -type d -empty -delete

Resources