Reputation: 15
Need to find all files in specific subdirectories ending in .xml
. They'll either be in .../etc/apps/<dirname>/local/data/ui/views/*.xml
or .../etc/apps/<dirname>/default/data/ui/views/*.xml
.
If the file in /etc/apps/<dirname>/default/data/ui/views/*.xml
doesn't have the string version="1.1"
in the first line, copy the file to the local directory as the same file name, add that string to the first line and quit.
I'm stuck on copying the file from its default dir to its local dir. I've got:
src=.../etc/apps/*/default/data/ui/views/*.xml
dest=.../etc/apps/*/local/data/ui/views/
pat='version='
for file in $src
do
if [[ "$(sed -n '1{/version=/p};q' "$file")" ]]; then
awk 'NR==1 {print; exit}' "$file"
echo "No change necessary to $file"
else
echo "Copying $file to local ../local/data/ui/views/$file and running sed '1 s/>/ version="1.1">/'"
fi
done
Which won't work because it's going to use the whole path for $file like I told it to. I get the feeling I'm overthinking this. But I'm not sure where to go from here.
Upvotes: 0
Views: 116
Reputation: 7277
Use grep like so:
grep -vril 'version="1.1"' .../etc/apps/*/default/data/ui/views/*.xml
It'll give you the list of files you without version="1.1"
Something like that may work:
while read file_path; do
file_name=$(basename "$file_path")
echo 'version="1.1"' > ./"$file_name"
cat "$file_path" >> ./"$file_name"
done < <(grep -vril 'version="1.1"' .../etc/apps/*/default/data/ui/views/*.xml)
Upvotes: 0
Reputation: 16960
A few remarks:
Instead of copy/modify the file, generating a new one in the local directory will be more efficient.
awk
can test the first line, print it, and even modify it, all at the same time.
Here's an idea of what you could do:
edit: handling the glob in the path and the possible pre-existence of the destination file
#!/bin/bash
shopt -s nullglob
for default_dir in .../etc/apps/*/default/data/ui/views/
do
local_dir=${default_dir/\/default\///local/} # replaces the first occurrence of "/default/" with "/local/"
for src_file in "$default_dir"*.xml
do
dest_file=${local_dir}${src_file##*/}
# we'll process the local file when it's present (instead of the default one)
[[ -e "$dest_file" ]] && src_file=$dest_file
if line=$( awk '
{
ok = /version=/
if (!ok)
sub( />/, "version=\"1.1\">" )
print
exit !ok
}
' "$src_file"
)
then
printf 'No change necessary to %q\n' "$src_file"
else
printf 'Generating %q' "$dest_file"
{ echo "$line"; cat "$src_file"; } > "$dest_file".tmp &&
mv "$dest_file".tmp "$dest_file"
fi
done
done
Upvotes: 1
Reputation: 7791
Here is one solution/approach with bash
and find
and ed
to edit the file in place, but change to something available in your system.
#!/usr/bin/env bash
str='version='
value=1.1
src=../etc/apps/default/data/ui/views
dest=../etc/apps/local/data/ui/views
while IFS= read -rd '' -u3 files; do
IFS= read -r first_line < "$files"
if [[ $first_line = *"$str"* ]]; then
printf 'No change necessary to %s\n' "$files"
else
printf "Copying %s to local %s\n" "$files" "$src/${files##*/}"
cp -v "$files" "$dest" || exit
printf 'Inserting %s at the first line of %s\n' "$str$value" "$dest/${files##*/}"
printf '%s\n' '0a' "<?xml $str${value}?>" . w q | ed -s "$dest/${files##*/}"
fi
done 3< <(find "$src" -type f -name '*.xml' -print0)
Create a dummy directories and files.
mkdir -vp ./etc/apps/default/data/ui/views/
mkdir -vp ./etc/apps/local/data/ui/views/
touch ./etc/apps/default/data/ui/views/{foo,bar,baz,more}.xml
echo 'version="1.1"' > ./etc/apps/default/data/ui/views/foo.xml
Check the created directories and files.
tree ./etc
Output
./etc/
└── apps
├── default
│ └── data
│ └── ui
│ └── views
│ ├── bar.xml
│ ├── baz.xml
│ ├── foo.xml
│ └── more.xml
└── local
└── data
└── ui
└── views
9 directories, 4 files
Check the contents of the files in the default directory.
tail -n+1 ./etc/apps/default/data/ui/views/*.xml
Output
==> ./etc/apps/default/data/ui/views/bar.xml <==
==> ./etc/apps/default/data/ui/views/baz.xml <==
==> ./etc/apps/default/data/ui/views/foo.xml <==
version="1.1"
==> ./etc/apps/default/data/ui/views/more.xml <==
Now execute your script but remove the first .
from the path, e.g.
src=./etc/apps/default/data/ui/views
dest=./etc/apps/local/data/ui/views
Output
Copying ./etc/apps/default/data/ui/views/bar.xml to local ./etc/apps/local/data/ui/views/bar.xml
'./etc/apps/default/data/ui/views/bar.xml' -> './etc/apps/local/data/ui/views/bar.xml'
Inserting version="1.1" at the first line of ./etc/apps/local/data/ui/views/bar.xml
Copying ./etc/apps/default/data/ui/views/more.xml to local ./etc/apps/local/data/ui/views/more.xml
'./etc/apps/default/data/ui/views/more.xml' -> './etc/apps/local/data/ui/views/more.xml'
Inserting version="1.1" at the first line of ./etc/apps/local/data/ui/views/more.xml
Copying ./etc/apps/default/data/ui/views/baz.xml to local ./etc/apps/local/data/ui/views/baz.xml
'./etc/apps/default/data/ui/views/baz.xml' -> './etc/apps/local/data/ui/views/baz.xml'
Inserting version="1.1" at the first line of ./etc/apps/local/data/ui/views/baz.xml
No change necessary to ./etc/apps/default/data/ui/views/foo.xml
Check the contents of directories.
tree ./etc
Output
./etc/
└── apps
├── default
│ └── data
│ └── ui
│ └── views
│ ├── bar.xml
│ ├── baz.xml
│ ├── foo.xml
│ └── more.xml
└── local
└── data
└── ui
└── views
├── bar.xml
├── baz.xml
└── more.xml
9 directories, 7 files
Check the file contents from the local directory.
tail -n+1 ./etc/apps/local/data/ui/views/*.xml
Output
==> ./etc/apps/local/data/ui/views/bar.xml <==
<?xml version="1.1"?>
==> ./etc/apps/local/data/ui/views/baz.xml <==
<?xml version="1.1"?>
==> ./etc/apps/local/data/ui/views/more.xml <==
<?xml version="1.1"?>
The script works as intended, just make an adjustment to your needs like Changing the ed
code from
printf '%s\n' '0a' "<?xml ${str}?>" . w q | ed -s
to your sed
or prefererably an xml editor/parser that can be scripted like xmlstarlet
and the likes.
As per the OP's comment including the *
glob in the path and an existing file to dest should not be overwritten. The *
does not expand on a variable assignment. An array
is needed for that.
#!/usr/bin/env bash
shopt -s nullglob
top_dirs=(./etc/apps/*/)
dest=local/data/ui/views/
src=default/data/ui/views/
shopt -u nullglob
str='version='
value=1.1
while IFS= read -rd '' -u3 files; do
IFS= read -r first_line < "$files"
##: skip files if it is in local/data/ui/views/, don't overwrite.
[[ -e "${files/default/local}" ]] && continue
if [[ $first_line = *"$str"* ]]; then
printf 'No change necessary to %s\n' "$files"
elif [[ $first_line != *"$str"* ]]; then
printf "Copying %s to local %s\n" "$files" "${files/default/local}"
cp -v "$files" "${files/default/local}"
printf 'Inserting %s at the first line of %s\n' "$str$value" "${files/default/local}"
printf '%s\n' '0a' "<?xml $str${value}?>" . w q | ed -s "${files/default/local}"
fi
done 3< <(find "${top_dirs[@]/%/"$src"}" -type f -name '*xml' -print0)
The above script was tested against the dummy files/directories with:
mkdir -vp ./etc/apps/dir{1..5}/default/data/ui/views/
mkdir -vp ./etc/apps/dir{1..5}/local/data/ui/views/
touch ./etc/apps/dir{1..5}/default/data/ui/views/{foo,bar,baz,more}.xml
echo 'version=' > ./etc/apps/dir1/default/data/ui/views/foo.xml
Upvotes: 0
Reputation: 998
You might want to use pipes and basename
. E.g.
ls <DIR>/*.xml | while read FULLPATH; do
SRC=$(basename $FULLPATH)
<your code>
done
Upvotes: 0