Modify date format in-place through `sed` command

331

Solution 1

If you have access to GNU date (the default on Linux systems), you can do:

$ sed -E 's/(.*)-([a-z]+)(.+)/\2\3-\1/i' file | 
    while read d; do date -d "$d" +%d/%m/%y; done
24/12/16
24/12/16

That changes lines like 2016-Dec-24 to Dec-24-2016 (a format that GNU date can understand), leaves lines like 2016-12-24 (a format GNU date already understands) alone, and then passes each line as an input date string to date. It doesn't do it in-place and it doesn't use sed -i but is almost certainly the simplest approach.

If you really need to do it using sed, you could make a list of all months and corresponding numbers:

$ for m in {1..12}; do printf '%s %s\n' "$m" $(date -d "$m/1/2016" +%b); done
1 Jan
2 Feb
3 Mar
4 Apr
5 May
6 Jun
7 Jul
8 Aug
9 Sep
10 Oct
11 Nov
12 Dec

Save that as months and then iterate over it to modify your file:

while read num mon; do 
    sed -Ei "s/$mon/$num/; s#(.*)-(.*)-(.*)#\3/\2/\1#" file
done < months 

Or, if your sed implementation needs separate -e:

while read num mon; do 
    sed -i -e "s/$mon/$num/" -Ee 's#(.*)-(.*)-(.*)#\3/\2/\1#' file
done < months 

The first substitution will replace all alphabetical month names with their corresponding number and the second moves things around to get your desired format.

Solution 2

The only thing I see wrong regex-wise is that in basic (BRE) mode, | is literal - you need \| to make it a logical OR i.e. \(01\|Jan\) and so on.

If your version supports -e then I don't see any good reason to make multiple calls to sed - you can just chain the -e <expr1> -e <expr2> ... in a single call. So

sed -i \
  -e 's/\([0-9]\{4\}\)-\(01\|Jan\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(02\|Feb\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(03\|Mar\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(04\|Apr\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(05\|May\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(06\|Jun\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(07\|Jul\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(08\|Aug\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(09\|Sep\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(10\|Oct\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(11\|Nov\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' \
  -e 's/\([0-9]\{4\}\)-\(12\|Dec\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1

However there are more elegant ways to do this - for example in perl using strptime and strftime functions (provided by the Time::Piece module for example):

perl -i -MTime::Piece -pe '
  s|\d{4}-\d\d-\d\d?|Time::Piece->strptime($&, "%Y-%m-%d")->strftime("%Y/%m/%d")|ge;
  s|\d{4}-...-\d\d?|Time::Piece->strptime($&, "%Y-%b-%d")->strftime("%Y/%m/%d")|ge;
' file
Share:
331
ghiboz
Author by

ghiboz

Updated on September 18, 2022

Comments

  • ghiboz
    ghiboz over 1 year

    I have this problem:

    I create in unity a dll with this code:

    public bool PackScripts(string dllPath, string[] scripts)
    {
        try
        {
            CodeDomProvider cc = CodeDomProvider.CreateProvider("CSharp");
            CompilerParameters cp = new CompilerParameters();
            var pf = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFiles);
            cp.ReferencedAssemblies.Add(string.Format(@"{0}\Unity\Editor\Data\Managed\UnityEngine.dll", pf));
            cp.GenerateExecutable = false; //generate executable
            cp.OutputAssembly = dllPath;
            CompilerResults cr = cc.CompileAssemblyFromFile(cp, scripts);
            return cr.Errors.Count == 0;
        }
        catch
        {
            return false;
        }
    }
    

    and works fine: if I create a solution with visual studio, include the dll created works fine!

    but my goal is to load this dll dynamically.. to do this I use this code:

    var assembly = System.Reflection.Assembly.Load("packScripts.dll");
    

    and here appears this error:

    FileNotFoundException: Could not load file or assembly 'C:/Users/Administrator/Documents/Unity/BundlesLoader/Assets/packscripts.dll' or one of its dependencies. The system cannot find the file specified.
    System.AppDomain.Load (System.String assemblyString, System.Security.Policy.Evidence assemblySecurity, Boolean refonly) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/AppDomain.cs:746)
    System.AppDomain.Load (System.String assemblyString) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/AppDomain.cs:728)
    (wrapper remoting-invoke-with-check) System.AppDomain:Load (string)
    System.Reflection.Assembly.Load (System.String assemblyString) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/Assembly.cs:576)
    Loader+<LoadBundle>c__Iterator0.MoveNext () (at Assets/Scripts/Loader.cs:94)
    

    Also if I create a new solution and try to use the same code, it gives the same error.. any ideas?

    thanks in advance!

    • Michael Vehrs
      Michael Vehrs over 7 years
      Of course, it is possible. What have you tried so far?
    • Alina Gorgovan
      Alina Gorgovan over 7 years
      I've edited the question and added an input example, sorry for the misunderstanding.
  • Michael Vehrs
    Michael Vehrs over 7 years
    Using date in a loop like that is inefficient. Use date -f instead.
  • smw
    smw over 7 years
    @don_crissti I'd kind of assumed the intent was to eventually replace the \2 backreference with the corresponding numeric month, as per the original question. That's why I left the OP's basic format as-is.