5

I need to replace single quotes with double quotes AND double quotes with single quotes in over 600 files. I've been reading various topics most of the day. I am hampered by only knowing some basic shell scripting commands and an unfamiliarity with regexp.

Below is a sample file I've made a bit generic.

   some text KEYWORD_1 table name column = "string" AND column = "string"
    Additional text
          .
          .
          .
   some text KEYWORD_2 text 'text in quote' etc. 

I only want to change the quotes on the 2 lines with KEYWORDs. I can't guarantee the keywords are always going to be first line and last line. I need my solution to find the keyword and change all the quotes on that line only.

I tried some things I found in sort of similar questions on this forum, including a meta on SED. I tried to figure out how to use this command: sed -i 's/foo\(.*baz\)/bar\1/' file. According to that topic the command, "Replaces foo with bar only if there is a baz later on the same line." My keyword is not later on the same line its at the beginning and I couldn't make it work for me.

I also tried ex -sc 'g/DEL/s/ALF/BRA/g' -cx file that didn't do anything. I'm assuming it can't be executed from the command line. I really don't want to open each of 600 files.

At one point I tried, grep KEYWORD_1 file | sed -i "s/'/\"/g" file
Of course, that changed all the quotes in the file not just the one line.

I'm betting the solution is simple and I just can't see it. How can I do this?

As requested my desired output is:

some text KEYWORD_1 table name column = 'string' AND column = 'string'
    Additional text
          . 
          .
          .
some text KEYWORD_2 text "text in quote" etc.
  • Could you please edit your question and show your desired output? My understanding is that you want to switch the ' to " and vice versa on all lines matching KEYWORD. However, as you can see from the answers below, others have understood something else. It would be clear if you showed your desired output. – terdon Jan 13 '17 at 22:57
  • Is it critical that a file is edited in place, i.e. without creating a copy? Typically, tools offering "in-place" edits actually write results to another file and then move it over the original one. – ivan_pozdeev Jan 14 '17 at 4:41
  • No, it's not critical that the file is edited in place. I can rename the copies if necessary. – Emily Shaffer Jan 16 '17 at 18:20
3

The complication here is that if you replace all ' with " and then all " with ', you will be left with only '. So, you will first need to replace ' with something else—for example, the NULL character (\0) which you can safely assume won't be in your input file—then " with ' and then replace that something else with " again. For example, in perl:

$ perl -pe "if(/KEYWORD){s/'/\0/g; s/\"/'/g; s/\0/\"/g}" file
KEYWORD_1 table name column = 'string' AND column = 'string'
Additional text
      .
      .
      .
KEYWORD_2 text "text in quote" etc. 

Explanation

  • -pe : print each input line after applying the script given by -e.
  • if(/KEYWORD/){something} : do something only if this line matches KEYWORD.
  • s/foo/bar/g : replace all occurrences of foo in the line with bar. The g means "global". Without it, only the 1st occurrence on each line would be replaced.

Note that since the script itself is inside double quotes, the double quotes inside the script need to be escaped (\").


As pointed out in the comments, there's a more direct way I should have thought of in the first place:

perl -pe "tr/'\"/\"'/ if /^KEYWORD/" file

The tr operator transliterates lists of characters. The general format is tr/searchlist/replacementlist/. So this will replace all ' with " and all " with ' only on lines matching KEYWORD.

  • I see this works as it prints the desired output but it doesn't save the changes to the existing file. I'm not opposed to creating a second file if necessary. – Emily Shaffer Jan 16 '17 at 18:05
  • @EmilyShaffer oh, I hadn't realized that was what you wanted. Just add the -i flag: perl -i -pe "tr/'\"/\"'/ if /^KEYWORD/" file – terdon Jan 16 '17 at 22:41
  • I tried this and it worked also. – Emily Shaffer Jan 20 '17 at 19:48
6

Changing characters

Changing a set of characters to another set of characters is generally a task for the tr command but since you want to do it on certain lines only, it will be best done by sed which has a y command similar to tr:

sed -e "/^KEYWORD_1/  y/\"/'/" \
    -e "/^KEYWORD_2/  y/'/\"/" \
    file

Each sed command here starts with a line selector /^KEYWORD/ which instructs sed to only operate on the line matching the pattern between /. Here the patterns start with the character ^ to indicate they are to be found at the beginning of the line.

Following the line selector is sed's substitution command y/set1/set2/g which replaces every occurrence of a character in set1 with the character which has the same position in set2.

Swapping characters

Now, if on the same line you want to replace each " with ' and at the same time each ' with ", you can use only one command:

sed -e "/^KEYWORD_1\|^KEYWORD_2/  y/\"'/'\"/" file
  • 1
    I think the OP wants to replace for all lines matching KEYWORD but change all ' with " and all " with '. I don't think we can rely in using KEYWORD1 or KEYWORD2 to know which replacement should be made. – terdon Jan 13 '17 at 22:52
  • I realized today there is text in front of my keywords. I tried your solutions without the ^ and the top line worked. When I tried the second line without a carat, I got the error sed: can't read FILENAME.txt: No such file or directory. I know the file exists because I used the first command on it, successfully. – Emily Shaffer Jan 16 '17 at 18:11
  • This is what I wound up using to change my files: sed -i -e "/^KEYWORD_1/ y/\"/'/" \ -i -e "/^KEYWORD_2/ y/'/\"/" \ file – Emily Shaffer Jan 16 '17 at 19:37
  • @EmilyShaffer Glad it worked. The altenate command is correct. Your error "can't read FILENAME.txt" was probably due to a quoting problem. If you mistakenly remove the leading or ending double quote, or one of the inner escapes (``) you may end up with a similar error. – xhienne Jan 16 '17 at 19:49
0

Slightly crude, but working GNU awk:

$ awk -v sq="'" '/KEYWORD/{if ($0~sq){ gsub(sq,"\"")} else if ($0~"\"") gsub(/\"/,sq)};1' input.txt                      
KEYWORD_1 table name column = 'string' AND column = 'string'
Additional text
      .
      .
      .
KEYWORD_2 text "text in quote" etc.

sq variable is used to represent single quotes, since embedding it into code block itself is a bit of a pain in awk. Basic idea is that we search for pattern, if pattern is found - we make decision what to replace. Final 1 simply tells awk to print the lines.

As for outputting it to file, simply use shell's > operator to redirect text into temporary file, and replace old file with new via mv command.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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