Bash text file editing/modifying

2019-03-06 10:46发布

I have a text file that I am trying to modify. I am taking the input file that has lines of the form of

(y+1/4,-x+1/2,z+3/4)

and trying to change it to

   0     1     0    -1     0     0     0     0     1    1 / 4    1 / 2    3 / 4

I currently can get to this point

   0     1     0 1/4    -1     0     0 1/2     0     0     1 3/4

using

#!bin/bash
filename="227.dat"
sed -i 's/(/  /g' $filename
sed -i 's/)//g' $filename
sed -i 's/,/    /g' $filename
sed -i 's/-x/-1     0     0/g' $filename
sed -i 's/x/ 1     0     0/g' $filename
sed -i 's/-y/ 0    -1     0/g' $filename
sed -i 's/y/ 0     1     0/g' $filename
sed -i 's/-z/ 0     0    -1/g' $filename
sed -i 's/z/ 0     0     1/g' $filename
sed -i '/+/! s/$/    0 \/ 1    0 \/ 1    0 \/ 1/' $filename

while ((i++)); read -r line; do
  if [[ $line == *[+]* ]]
    then
      sed -i 's/+/ /g' $filename
      echo $i
  fi
done < "$filename"

The reason for the echo $i was to see that it correctly gives the line number and I thought perhaps I could use it for commands on those specific lines. I am doing this conversion as the code we use in creating crystal structures needs the vector notation with fractions at the end, not the x,y,z notation. I already know this is not the "prettiest" or simplest solution, but I am very new to all of this and it's what I have been able to piece together so far. Any suggestions?

标签: bash shell
2条回答
你好瞎i
2楼-- · 2019-03-06 11:12
#!/usr/bin/env bash

filename="227.dat"

re='[(]y[+]([[:digit:]/]+),-x[+]([[:digit:]/]+),z[+]([[:digit:]/]+)[)]';
while IFS= read -r line; do
    if [[ $line =~ $re ]]; then
        printf '\t%s' \
            0 1 0 \
           -1 0 0 \
            0 0 1 \
            "${BASH_REMATCH[1]}" \
            "${BASH_REMATCH[2]}" \
            "${BASH_REMATCH[3]}";
        printf '\n';
    else
        echo "ERROR: $line does not match $re" 1>&2;
    fi;
done <"$filename"

...given, your input, returns:

   0       1       0       -1      0       0       0       0       1       1/4     1/2     3/4

...which as far as I can tell is correct.


A more complex approach, making unfounded extrapolations (given the lack of detail and exemplars in the question itself), might look like:

#!/usr/bin/env bash
while IFS='(),' read -a pieces; do
  declare -A vars=( [x]=1 [y]=1 [z]=1 [x_sigil]='' [y_sigil]='' [z_sigil]='' )
  for piece in "${pieces[@]}"; do
    #                1   2      3   4
    if [[ $piece =~ (-?)([xyz])([+]([[:digit:]/]+))? ]]; then
      if [[ ${BASH_REMATCH[4]} ]]; then                 # only if there *are* digits
        vars[${BASH_REMATCH[2]}]=${BASH_REMATCH[4]}     # ...then store them.
      fi
      vars[${BASH_REMATCH[2]}_sigil]=${BASH_REMATCH[1]} # store - if applicable
    fi
  done
  printf '\t%s' \
    "0"                 "${vars[x_sigil]}1" 0 \
    "${vars[y_sigil]}1" 0                   0 \
    0                   0                   "${vars[z_sigil]}1" \
    "${vars[y]}"        "${vars[x]}"        "${vars[z]}"
  printf '\n'
done

Given the sample inputs provided in a comment on this answer, output is:

0   1   0   1   0   0   0   0   1   1   1   1
0   1   0   1   0   0   0   0   1   1   1   1
0   1   0   1   0   0   0   0   1   1   1   1
0   1   0   1   0   0   0   0   -1  3/4 1/4 1/2
0   1   0   -1  0   0   0   0   1   1/2 3/4 1/4
0   -1  0   1   0   0   0   0   1   1/4 1/2 3/4
0   -1  0   -1  0   0   0   0   -1  1   1   1
0   -1  0   -1  0   0   0   0   -1  1   1   1
0   -1  0   -1  0   0   0   0   -1  1   1   1
0   -1  0   -1  0   0   0   0   1   1/4 3/4 1/2
0   -1  0   1   0   0   0   0   -1  1/2 1/4 3/4
0   1   0   -1  0   0   0   0   -1  3/4 1/2 1/4
0   -1  0   -1  0   0   0   0   1   1/4 3/4 1/2
0   -1  0   -1  0   0   0   0   1   1/4 3/4 1/2
0   -1  0   -1  0   0   0   0   1   1/4 3/4 1/2
0   -1  0   -1  0   0   0   0   -1  1   1   1
0   -1  0   1   0   0   0   0   1   1/4 1/2 3/4
0   1   0   -1  0   0   0   0   1   1/2 3/4 1/4
0   1   0   1   0   0   0   0   -1  3/4 1/4 1/2
0   1   0   1   0   0   0   0   -1  3/4 1/4 1/2
0   1   0   1   0   0   0   0   -1  3/4 1/4 1/2
0   1   0   1   0   0   0   0   1   1   1   1
0   1   0   -1  0   0   0   0   -1  3/4 1/2 1/4
0   -1  0   1   0   0   0   0   -1  1/2 1/4 3/4
0   -1  0   1   0   0   0   0   -1  1/2 1/4 3/4
0   -1  0   1   0   0   0   0   -1  1/2 1/4 3/4
0   -1  0   1   0   0   0   0   -1  1/2 1/4 3/4
0   -1  0   1   0   0   0   0   1   1/4 1/2 3/4
查看更多
Luminary・发光体
3楼-- · 2019-03-06 11:27

Here's an approach that may simplify the parsing. Read each line into an array using IFS set to all possible delimiters and characters you don't care about:

while IFS=$'\(\)+,' read -ra line; do
    for i in 1 3 5; do
        case "${line[$i]}" in
            x) printf "%s\t%s\t%s\t" 1 0 0 ;;
            y) printf "%s\t%s\t%s\t" 0 1 0 ;;
            z) printf "%s\t%s\t%s\t" 0 0 1 ;;
            -x) printf "%s\t%s\t%s\t" -1 0 0 ;;
            -y) printf "%s\t%s\t%s\t" 0 -1 0 ;;
            -z) printf "%s\t%s\t%s\t" 0 0 -1 ;;
        esac
    done
    for i in 2 4 6; do
        printf "%s\t" "${line[$i]}"
    done
    echo
done < "$filename"
查看更多
登录 后发表回答