So,
is it possible to modify a files which are uploaded into Files in mikrotik routers via cli? I need to change some link inside it.
I can access it via
file edit value-name=contents test.txt
but how to change it there through terminal
So,
is it possible to modify a files which are uploaded into Files in mikrotik routers via cli? I need to change some link inside it.
I can access it via
file edit value-name=contents test.txt
but how to change it there through terminal
you already wrote the command to do it from terminal. what is the issue with this command?
/file set test.txt contents="Did you want to write stuff at the CLI prompt?"
No, i have a file name test.txt and in that file I need to change some url, so instead of http to use https, thats the only change.
But i cannot fetch it again, cause every file is different
Sorry, still not understanding your issue.
issue command “file edit value-name=contents test.txt” and change your URL. what’s the problem?
problem is that i have more than 1000 routers, and dont wanna do it manualy, so i will use automation to do it, and thats why i need a single command.
There is no way to do it in the router. You need a utility like “sed”.
so its not possible something like this thread:
http://forum.mikrotik.com/t/edit-script-from-terminal/56614/1
Did you read the thread? The person answers the same thing as I answered now. You can’t do it. RouterOS has no sed utility. You can only overwrite the whole file with new content. All of it.
yes i did, but i thought its possible to change it. So, any ideas?
Well you would not be the first to want such a function… see 2018 thread: http://forum.mikrotik.com/t/built-in-function-library/117288/1
It’s not easy, and limited. But you can :find and :pick your way through it… see http://forum.mikrotik.com/t/change-word-in-comment/163850/1
i somehow stuck here
/file get test.html contents
/file set test.html contents=[/file get test.html contents; :find "http://"; :pick replace with "https://"]
but there is no replace…of course..
Download file, replace locally with e.g. sed, re-upload file. Can be scripted using REST API for example. Is there anything that hinders you following this approach? APIs not accessible and it must be done locally as a ROS script?
The reason why @normis suggested RouterOS is missing sed
is that it’s a lot of script code just to parse it. But I dug up a function that does it, at least in SOME cases. This one is borrowed from @merlinthemagic7 larger script library that I made into a simple function so it not require the rest of MTM’s library. I have not test in a while, but seem to still work.
While the function below seems to have held up in the test of time… these large function do introduce risk if you don’t know how they work. And if some future RouterOS changing script in certain way, it’s possible it will break. Why others, and me, really suggest doing your string parsing elsewhere i.e. before the file copy to router – RouterOS does NOT have primitives for string manipulation, as @normis notes several times.
So if you include this script before you need to do a replacement, then you you can some a more simple like:
:put [$STR replace "http://srv1/,http://srv2" "http://" "https://"]
# https://srv1/,https://srv2
to do the replacement.
Here is the $STR function which support “trim”, “split”, and “replace” as operations:
### $STR - string helpers
# from: https://raw.githubusercontent.com/merlinthemagic/MTM-RouterOS-Scripting/main/src/flash/MTM/Tools/Strings/Part1.rsc
:global STR
:set STR do={
:if ($1="trim") do={
:local param1
:if ([:typeof $2] != nil) do={
:set param1 $2;
} else={
:if ([:typeof $str] != nil) do={
:set param1 $str
}
:error "String is mandatory";
}
:set param1 [:tostr $param1]; #ROS casts lots of things as arrays. e.g. rx/tx data from interfaces
:local rLen [:len $param1];
:local rData "";
:local ch "";
:local isDone 0;
# remove leading spaces
:for x from=0 to=($rLen - 1) do={
:set ch [:pick $param1 $x];
:if ($isDone = 0 && $ch != " " && $ch != "\n" && $ch != "\r") do={
:set rData [:pick $param1 $x $rLen];
:set isDone 1;
}
}
:set rLen [:len $rData];
:local cPos $rLen;
:set isDone 0;
# remove trailing spaces
:for x from=1 to=($rLen - 1) do={
:set cPos ($rLen - $x);
:set ch [:pick $rData $cPos];
:if ($isDone = 0 && $ch != " " && $ch != "\n" && $ch != "\r") do={
:set rData [:pick $rData 0 ($cPos + 1)];
:set isDone 1;
}
}
:if ($rData = [:nothing]) do={
#always return string, the nil value is a pain
:set rData "";
}
:return $rData;
}
:if ($1="replace") do={
:local param1; #str
:local param2; ##find
:local param3; ##replace
:if ([:typeof $1] != nil) do={
:set param1 $2;
:set param2 $3;
:set param3 $4;
} else={
:if ([:typeof $str] != nil) do={
:set param1 $str;
:set param2 $find;
:set param3 $replace;
} else={
:error "String is mandatory";
}
}
:set param1 [:tostr $param1]; #ROS casts lots of things as arrays. e.g. rx/tx data from interfaces
:local rData "";
:local pos;
:local rLen [:len $param1];
:local findLen [:len $param2];
:local isDone 0;
:while ($isDone = 0) do={
:set pos [:find $param1 $param2];
:if ([:typeof $pos] = "num") do={
:set rData ($rData.[:pick $param1 0 $pos].$param3);
:set param1 [:pick $param1 ($pos + $findLen) $rLen];
:set rLen [:len $param1];
} else={
:set rData ($rData.$param1);
:set isDone 1;
}
}
:return $rData;
}
:if ($1="split") do={
:local param1; #input
:local param2; #deliminator
:if ([:typeof $2] != nil) do={
:set param1 $2;
#delemitor= case ...
:set param2 $3;
} else={
:if ([:typeof $str] != nil) do={
:set param1 $str;
:set param2 $delimitor;
} else={
:error "String is mandatory";
}
}
:local rData [:toarray ""];
:local rCount 0;
:local splitLen [:len $param2];
:if ($splitLen = 0) do={
:set ($rData->$rCount) $param1;
:return $rData;
}
:local lData "";
:local rLen [:len $param1];
:local pos;
:local isDone 0;
:while ($isDone = 0) do={
:set pos [:find $param1 $param2];
:if ([:typeof $pos] = "num") do={
:set lData [:pick $param1 0 $pos];
:set param1 [:pick $param1 ($pos + $splitLen) $rLen];
:set rLen [:len $param1];
} else={
:set lData $param1;
:set isDone 1;
}
:set ($rData->$rCount) $lData;
:set rCount ($rCount + 1);
}
:return $rData;
}
}
You can cut-and-paste that at CLI to test. And then use:
:global replacedContent [$STR replace [/file get FILENAME contents] "http://" "https://"]
# to see the output
:put $replacedContent
# to save output to a new file
/file add name=NEWFILENAME
/file set NEWFILENAME contents=$replacedContent
# if it wanted same file - which is bad for testing - don't need the /file add
# /file set FILENAME contents=$replacedContent