1

I am facing some weird issue with sed command while replacing contents within a file. I created a dummy file with few lines and tried to execute my script. It worked.

The same script fails to replace the string when I use the lvm.conf file.

Task is to append disk path to the global_filter in lvm.conf file.

Input will be /dev/sdb1 and I have to add the same to the global_filter global_filter = ["a|^/dev/sda2$|", "r/.*/"]

My script is as follows:

#!/bin/sh

function addToFilter(){
    app_lvm_conf="/etc/lvm/lvm.conf"
    line=$(sed -n '/global_filter =/p' $app_lvm_conf)
    echo $line
    exstngfltr=$(echo $line | cut -d "[" -f2 | cut -d "]" -f1)
    echo $exstngfltr
    IFS=', ' read -r -a array <<< "$exstngfltr"
    echo ${array[@]}
    numOfEntries=${#array[@]}
    echo $numOfEntries
    value=$(IFS=,; echo "${array[*]}")
    echo $value
    temp="${array[numOfEntries-1]}"
    echo $temp

    ar=$1
    echo $ar
    a='"a|^'
    b='$|"'
    z=$a$1$b
    echo $z
    array[numOfEntries-1]=$z
    array[numOfEntries]=$temp
    newValue=$(IFS=,; echo "${array[*]}")
    sed -i "s@$value@$newValue@g" $app_lvm_conf
}
addToFilter $1

I have used multiple echo statements to see the output, I will remove them once the script is working fine.


Input is "/dev/sda2"

The expected output in lvm.conf file is:

global_filter = ["a|^/dev/sda1$|","a|^/dev/sda2$|","r/.*/"]

There will be one global_filter entry in lvm.conf file. On execution of this script, that filter should get updated.

Since that file is huge, I have copy pasted the section where global_filter is present into separate file and working on the same.

lvm.conf file

# Since "filter" is often overridden from command line, it is not suitable
# for system-wide device filtering (udev rules, lvmetad). To hide devices
# from LVM-specific udev processing and/or from lvmetad, you need to set
# global_filter. The syntax is the same as for normal "filter"
# above. Devices that fail the global_filter are not even opened by LVM.

global_filter = ["a|^/dev/dasda2$|", "r/.*/"]

# The results of the filtering are cached on disk to avoid
# rescanning dud devices (which can take a very long time).
# By default this cache is stored in the /etc/lvm/cache directory
# in a file called '.cache'.

On execution of the script, the file should change as below

# Since "filter" is often overridden from command line, it is not suitable
# for system-wide device filtering (udev rules, lvmetad). To hide devices
# from LVM-specific udev processing and/or from lvmetad, you need to set
# global_filter. The syntax is the same as for normal "filter"
# above. Devices that fail the global_filter are not even opened by LVM.

global_filter = ["a|^/dev/sda1$|", "a|^/dev/sda2$|", "r/.*/"]

# The results of the filtering are cached on disk to avoid
# rescanning dud devices (which can take a very long time).
# By default this cache is stored in the /etc/lvm/cache directory
# in a file called '.cache'.

The input to the script should be strictly of type /dev/sda2.

closed as unclear what you're asking by Kusalananda, G-Man, Stephen Rauch, Anthon, Archemar Sep 25 '17 at 6:40

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    Yes, please show us your input and your desired output. Your script is far too complex for such a simple task. – terdon Feb 10 '17 at 12:27
  • I don't fully understand what you are trying to do but in value=$(IFS=,; echo "${array[*]}") the assignment to IFS is most definitely erroneous -- the ; is a command separator and the assignment won't affect the environment of echo. – AlexP Feb 10 '17 at 12:47
  • @AlexP - he's trying to add a filter to lvm.conf. The approach is far from ideal though... This can be done entirely in sed but requires some escaping... and -to make life easier- passing the exact filter to the script instead of just the device name. – don_crissti Feb 10 '17 at 13:00
  • @don_crissti: The point is that the assignment to IFS is a no-operation. – AlexP Feb 10 '17 at 13:05
  • 2
    @AlexP it's a way to get ${array[*]} to expand with comma separators in place of the default spaces - at least in bash. The IFS assignment is in the same subshell as the array expansion, so it does affect it. Try it ;) – steeldriver Feb 10 '17 at 13:25
0

I believe this does what you need:

addition=', "a|^/dev/sda2$|"'
expression='/global_filter/ s@(, "r)@'"$addition"\\1'@'
sed -r -i.orig "${expression}" /etc/lvm.conf

The trick here is using the pattern matching of sed to match the line with /global_filter/, and then using the search/replace, to just replace the bit around the comma: , "r

with your new addition.

The '-r' flag is required here for sed in order to capture the ' , "r ' part with the parentheses '(pattern)' so that you can then re-insert it after your addition, using the backreference "\1"

Or, simpler to understand, without the -r and '()' matching:

addToFilter()
{
  device="${1}"
  addition='"a|^'"${device}"'$|"'

  insert_before=', "r'

  replacement=", ${addition}${insert_before}"

  line_match="global_filter"

  expression="/${line_match}/ s@${insert_before}@${replacement}@"

  sed -i.orig -e "${expression}" /etc/lvm.conf
} 

addToFilter "/dev/sda2"

I have assumed that 'dev/dasda2' in your example input file is not the true input, but was left there from a failed edit.

Not the answer you're looking for? Browse other questions tagged or ask your own question.