143

I have a code which is:

DECLARE @Script VARCHAR(MAX)

SELECT @Script = definition FROM manged.sys.all_sql_modules sq
where sq.object_id = (SELECT object_id from managed.sys.objects 
Where type = 'P' and Name = 'usp_gen_data')

Declare @Pos int

SELECT  @pos=CHARINDEX(CHAR(13)+CHAR(10),@script,7500)

PRINT SUBSTRING(@Script,1,@Pos)

PRINT SUBSTRING(@script,@pos,8000)

The length of the Script is around 10,000 Characters and Since I am using print Statement which can hold only max of 8000. So I am using two print statements.

The problem is when I have a script which is of say 18000 characters then I used to use 3 print statements.

So Is there a way that I could set the number of print statements depending on the length of the script?

Kelsey
  • 47,246
  • 16
  • 124
  • 162
peter
  • 2,396
  • 6
  • 24
  • 29

18 Answers18

274

I know it's an old question, but what I did is not mentioned here.

For me the following worked (for up to 16k chars)

DECLARE @info NVARCHAR(MAX)

--SET @info to something big

PRINT CAST(@info AS NTEXT)

If you have more than 16k chars you can combine with @Yovav's answer like this (64k should be enough for anyone ;)

    print cast( substring(@info, 1, 16000) as ntext )
    print cast( substring(@info, 16001, 32000) as ntext )
    print cast( substring(@info, 32001, 48000) as ntext )
    print cast( substring(@info, 48001, 64000) as ntext )
Rory
  • 40,559
  • 52
  • 175
  • 261
alfoks
  • 4,324
  • 4
  • 29
  • 44
  • Did anyone apply this method to real data with success? I'm using SQL Server 2008 R2 SP2. SQL Server Management Studio as a client. `PRINT` documentation seems to promise truncation to 4K/8K characters (ntext/text), but strangely, I only see truncation to 16K characters which I'm not sure where it is coming from and how to disable it. – Jirka Hanika Jan 03 '13 at 17:22
  • @JirkaHanika I see the same truncation at 16k using convert(text, @vmax) – gordy Jan 10 '13 at 21:17
  • 6
    @gordy - So it seems to me that this method does not really work in SSMS. – Jirka Hanika Jan 11 '13 at 08:02
  • Worked fine for me (on my rather backwards setup) of SSMS 2012 and SQL 2005 mode 80. – Alex KeySmith Jun 27 '13 at 15:35
  • 1
    This works for me on SQL 2008 R2 SP2 (10.50.1600) using either CAST() or CONVERT(), and on SQL 2008 SP2 (10.0.5500). –  Nov 05 '13 at 10:13
  • 28
    I see truncation after 16,002 characters, still longer than `max` though. `DECLARE @info NVARCHAR(MAX) = 'A';SET @info = REPLICATE(@info, 16000) + 'BC This is not printed';PRINT @info;PRINT CAST(@info AS NTEXT);` – Martin Smith Dec 24 '13 at 20:59
  • 1
    For those of you who are able to run this successfully, please specify what client you're using and what settings you're using for query results. – GaTechThomas Oct 14 '14 at 17:43
  • Thanks for the answer! It helped me! – Pedram Dec 16 '15 at 05:18
  • Worked for me in SQL Server 2014 using SSMS 2014 – izzy Jan 28 '16 at 12:50
  • 8
    [ntext , text, and image data types](https://msdn.microsoft.com/en-us/library/ms187993(v=sql.110).aspx) will be removed in a future version of Microsoft SQL Server. Avoid using these data types in new development work, and plan to modify applications that currently use them. – jumxozizi Aug 11 '16 at 12:54
  • Worked in SQL 1016 SSMS build 13.0.16100.1 running against SQL 2016. I went with a cursor to avoid future issues and the potential of the string values going over 16k characters. – Chris Porter Jan 04 '17 at 23:33
  • Ah yes, the default limit is 2 MB. My string cut off at 2,097,145 characters (7 characters shy of 2 MB). Thanks for the note about changing the configuration value for max XML size. – ErikE Mar 23 '17 at 23:36
  • 5
    Didn't work for me in SQL Server Management Studio for SQL Server 2014. It cuts after 16.000 characters. As written by Martin Smith. – Jana Weschenfelder Jan 18 '18 at 15:21
  • Should add TLDR to your answer... – Dean Jun 22 '22 at 08:29
128

The following workaround does not use the PRINT statement. It works well in combination with the SQL Server Management Studio.

SELECT CAST('<root><![CDATA[' + @MyLongString + ']]></root>' AS XML)

You can click on the returned XML to expand it in the built-in XML viewer.

There is a pretty generous client side limit on the displayed size. Go to Tools/Options/Query Results/SQL Server/Results to Grid/XML data to adjust it if needed.

Jirka Hanika
  • 13,301
  • 3
  • 46
  • 75
  • 12
    +1. But this method encodes characters that have a special meaning in XML. For example, `<` is replaced with `<`. – Iain Samuel McLean Elder Mar 19 '13 at 18:06
  • 6
    you can write script without `....` like: `SELECT CAST(@MyLongString AS XML)` – Ali U Mar 30 '15 at 09:25
  • 2
    @aliyouhannaei - Yes and no. You are right that the root element isn't strictly necessary. But, without the CDATA section, your method starts having trouble with some strings. Especially those that contain <. If they aren't XML, the query will usually error out. If they are XML, the string may end up reformatted into another "equivalent" XML form. – Jirka Hanika Sep 07 '15 at 06:16
  • 10
    @IainElder - That's a good point and there's a workaround for it [from Adam Machanic](http://stackoverflow.com/a/2760023/1235565). It's this: `SELECT @MyLongString AS [processing-instruction(x)] FOR XML PATH('')`. The string will be wrapped in a PI called "x", but the PI won't be wrapped in another element (because of `PATH('')`). – Jirka Hanika Sep 07 '15 at 06:25
  • 1
    This won't work for very long texts, even with "Maximum Characters Retrieved - XML data" set to unlimited – Michael Møldrup Jan 24 '18 at 11:12
  • @MichaelMøldrup - Make sure that your Tools/Options/Query Results/SQL Server/General has Default destination for results set to Results to Grids. If still no luck, elaborate on how long was your data and how much of it was displayed. – Jirka Hanika Jan 24 '18 at 15:05
  • This answer doesn't work. The data still gets truncated. Happened around 3900 characters for me. – MgSam Aug 07 '20 at 19:20
  • @MgSam - Try printing the length of your string first and then the client side limit described in the answer. – Jirka Hanika Aug 08 '20 at 19:22
45

Here is how this should be done:

DECLARE @String NVARCHAR(MAX);
DECLARE @CurrentEnd BIGINT; /* track the length of the next substring */
DECLARE @offset tinyint; /*tracks the amount of offset needed */
set @string = replace(  replace(@string, char(13) + char(10), char(10))   , char(13), char(10))

WHILE LEN(@String) > 1
BEGIN
    IF CHARINDEX(CHAR(10), @String) between 1 AND 4000
    BEGIN
           SET @CurrentEnd =  CHARINDEX(char(10), @String) -1
           set @offset = 2
    END
    ELSE
    BEGIN
           SET @CurrentEnd = 4000
            set @offset = 1
    END   
    PRINT SUBSTRING(@String, 1, @CurrentEnd) 
    set @string = SUBSTRING(@String, @CurrentEnd+@offset, LEN(@String))   
END /*End While loop*/

Taken from http://ask.sqlservercentral.com/questions/3102/any-way-around-the-print-limit-of-nvarcharmax-in-s.html

Ben B
  • 779
  • 6
  • 4
  • 1
    Great technique! BTW, the actual article which originated this technique was from SQLServerCentral.com >>> http://www.sqlservercentral.com/scripts/Print/63240/ – Rob.Kachmar Jul 24 '14 at 17:14
  • 2
    This worked for me, but it also chopped one of my field names in half. So, if I used this method to PRINT (@string) and then EXECUTE (@string), the EXECUTE fails. – Johnny Bones Mar 04 '16 at 15:53
  • 1
    This doesn't work for me as the PRINT function adds line breaks in bad places and would require more cleanup than it's worth but this is the closest solution to the problem. – Randy Burden Sep 14 '17 at 19:14
  • Finally a script that works !! I know it's +10 years since your comment but well done and thank you ! – Marc Chemali Aug 25 '23 at 12:08
30

You could do a WHILE loop based on the count on your script length divided by 8000.

EG:

DECLARE @Counter INT
SET @Counter = 0
DECLARE @TotalPrints INT
SET @TotalPrints = (LEN(@script) / 8000) + 1
WHILE @Counter < @TotalPrints 
BEGIN
    -- Do your printing...
    SET @Counter = @Counter + 1
END
Kelsey
  • 47,246
  • 16
  • 124
  • 162
  • If you look at my code I am also using the @Pos variable to find the line break and print accordingly. So How could I use that in your code. – peter Oct 21 '11 at 14:11
  • @peter You can just take the current `SUBSTR` and look at only the part you are dealing with at the time and iterate on that or if you know that there will be a line break before the 8k limit each time then just do the `WHILE` based on finding line breaks. – Kelsey Oct 21 '11 at 14:13
  • @peter can you loop based on the line breaks? Eg look for linebreak, if found print up to line break, substr from line break to next 8k chars, search, print, new substr etc? – Kelsey Oct 21 '11 at 14:24
  • Yes I can look for line break but the problem is: I am looking for line breaks after 7500 characters so in this case I found line break at 7567 character so First I am printing from 1 to 7567 characters and then I am printing from 7567 to reamining characters but I should find the second line break from 7567+7500 characters and keep on doing that. How do i do that – peter Oct 21 '11 at 14:29
  • @peter In your while loop, save the position you last found the line break and then do a SUBSTR with the last found line break as the starting position + 7500. That will give you the next string to search within and then keep doing the same logic until you are beyond the length of the script string. – Kelsey Oct 21 '11 at 16:00
  • Caution, if script is SQL code, this will BREAK up a statement right in the middle... Example: SELECT ISN (newline here) ULL(SUM(Whatever), 0.0) – Stefan Steiger Dec 20 '11 at 08:14
  • I would strong advise against using this solution. The correct technique is below from @ben-b BTW, LENGTH() isn't even a function in SQL Server – Rob.Kachmar Jul 24 '14 at 17:18
  • 1
    The function is LEN() not LENGTH() – shiggity Aug 10 '17 at 14:39
  • 14
    I used `print(substring(@script, @Counter * 8000, (@Counter + 1) * 8000))` to print my script. – Lukas Thum Oct 09 '18 at 12:24
  • 1
    I inserted the print statement before the counter increment, `print substring(@script, (@Counter * 8000) + 1, 8000)` – José Margaça Lopes Dec 16 '20 at 17:49
16

Came across this question and wanted something more simple... Try the following:

SELECT [processing-instruction(x)]=@Script FOR XML PATH(''),TYPE
Edyn
  • 2,409
  • 2
  • 26
  • 25
  • 6
    More simple would be `SELECT CAST(@STMT AS XML)` as already stated in another comment. Produces exactly the same output and is indeed less complicated than creating a stored procedure for output. – Felix Bayer May 05 '15 at 08:30
  • 5
    @Felix While that would be much simpler, it doesn't quite work for SQL. Casting to XML tries to convert the SQL text to XML. It will replace <, >, and & with <, > and & and it won't handle chars not allowed in XML. Additionally, if you have a situation where you do a comparison of < and then >, it thinks that's an element and throws an invalid node error. – Edyn May 05 '15 at 14:40
13

I just created a SP out of Ben's great answer:

/*
---------------------------------------------------------------------------------
PURPOSE   : Print a string without the limitation of 4000 or 8000 characters.
https://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
USAGE     : 
DECLARE @Result NVARCHAR(MAX)
SET @Result = 'TEST'
EXEC [dbo].[Print_Unlimited] @Result
---------------------------------------------------------------------------------
*/
ALTER PROCEDURE [dbo].[Print_Unlimited]
    @String NVARCHAR(MAX)
AS

BEGIN

    BEGIN TRY
    ---------------------------------------------------------------------------------

    DECLARE @CurrentEnd BIGINT; /* track the length of the next substring */
    DECLARE @Offset TINYINT; /* tracks the amount of offset needed */
    SET @String = replace(replace(@String, CHAR(13) + CHAR(10), CHAR(10)), CHAR(13), CHAR(10))

    WHILE LEN(@String) > 1
    BEGIN
        IF CHARINDEX(CHAR(10), @String) BETWEEN 1 AND 4000
        BEGIN
            SET @CurrentEnd =  CHARINDEX(CHAR(10), @String) -1
            SET @Offset = 2
        END
        ELSE
        BEGIN
            SET @CurrentEnd = 4000
            SET @Offset = 1
        END   
        PRINT SUBSTRING(@String, 1, @CurrentEnd) 
        SET @String = SUBSTRING(@String, @CurrentEnd + @Offset, LEN(@String))   
    END /*End While loop*/

    ---------------------------------------------------------------------------------
    END TRY
    BEGIN CATCH
        DECLARE @ErrorMessage VARCHAR(4000)
        SELECT @ErrorMessage = ERROR_MESSAGE()    
        RAISERROR(@ErrorMessage,16,1)
    END CATCH
END
Yovav
  • 2,557
  • 2
  • 32
  • 53
12

This proc correctly prints out VARCHAR(MAX) parameter considering wrapping:

CREATE PROCEDURE [dbo].[Print]
    @sql varchar(max)
AS
BEGIN
    declare
        @n int,
        @i int = 0,
        @s int = 0, -- substring start posotion
        @l int;     -- substring length

    set @n = ceiling(len(@sql) / 8000.0);

    while @i < @n
    begin
        set @l = 8000 - charindex(char(13), reverse(substring(@sql, @s, 8000)));
        print substring(@sql, @s, @l);
        set @i = @i + 1;
        set @s = @s + @l + 2; -- accumulation + CR/LF
    end

    return 0
END
Andrey Morozov
  • 7,839
  • 5
  • 53
  • 75
9

I was looking to use the print statement to debug some dynamic sql as I imagin most of you are using print for simliar reasons.

I tried a few of the solutions listed and found that Kelsey's solution works with minor tweeks (@sql is my @script) n.b. LENGTH isn't a valid function:

--http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
--Kelsey
DECLARE @Counter INT
SET @Counter = 0
DECLARE @TotalPrints INT
SET @TotalPrints = (LEN(@sql) / 4000) + 1
WHILE @Counter < @TotalPrints 
BEGIN
    PRINT SUBSTRING(@sql, @Counter * 4000, 4000)
    SET @Counter = @Counter + 1
END
PRINT LEN(@sql)

This code does as commented add a new line into the output, but for debugging this isn't a problem for me.

Ben B's solution is perfect and is the most elegent, although for debugging is a lot of lines of code so I choose to use my slight modification of Kelsey's. It might be worth creating a system like stored procedure in msdb for Ben B's code which could be reused and called in one line?

Alfoks' code doesn't work unfortunately because that would have been easier.

Matthew R
  • 1,038
  • 11
  • 15
  • I just added Ben B's solution as a temporary stored procedure. Keeps my scripts a bit cleaner, but I agree that it's a lot of lines for debugging. – Zarepheth Apr 06 '17 at 16:03
7

Or simply:

PRINT SUBSTRING(@SQL_InsertQuery, 1, 8000)
PRINT SUBSTRING(@SQL_InsertQuery, 8001, 16000)
Yovav
  • 2,557
  • 2
  • 32
  • 53
  • You could extend Yavav's suggestion here and simply use many 8000 char chunks... covering your worst case scenario... So if @SQL_InsertQuery was actually short just the first few lines print, the remaining ones just print an empty string – andrew pate Mar 03 '21 at 11:28
6

You can use this

declare @i int = 1
while Exists(Select(Substring(@Script,@i,4000))) and (@i < LEN(@Script))
begin
     print Substring(@Script,@i,4000)
     set @i = @i+4000
end
Marwan Almukh
  • 201
  • 3
  • 12
4

There is great function called PrintMax written by Bennett Dill.

Here is slightly modified version that uses temp stored procedure to avoid "schema polution"(idea from https://github.com/Toolien/sp_GenMerge/blob/master/sp_GenMerge.sql)

EXEC (N'IF EXISTS (SELECT * FROM tempdb.sys.objects 
                   WHERE object_id = OBJECT_ID(N''tempdb..#PrintMax'') 
                   AND type in (N''P'', N''PC''))
    DROP PROCEDURE #PrintMax;');
EXEC (N'CREATE PROCEDURE #PrintMax(@iInput NVARCHAR(MAX))
AS
BEGIN
    IF @iInput IS NULL
    RETURN;

    DECLARE @ReversedData NVARCHAR(MAX)
          , @LineBreakIndex INT
          , @SearchLength INT;

    SET @SearchLength = 4000;

    WHILE LEN(@iInput) > @SearchLength
    BEGIN
    SET @ReversedData = LEFT(@iInput COLLATE DATABASE_DEFAULT, @SearchLength);
    SET @ReversedData = REVERSE(@ReversedData COLLATE DATABASE_DEFAULT);
    SET @LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13),
                          @ReversedData COLLATE DATABASE_DEFAULT);
    PRINT LEFT(@iInput, @SearchLength - @LineBreakIndex + 1);
    SET @iInput = RIGHT(@iInput, LEN(@iInput) - @SearchLength 
                        + @LineBreakIndex - 1);
    END;

    IF LEN(@iInput) > 0
    PRINT @iInput;
END;');

DBFiddle Demo

EDIT:

Using CREATE OR ALTER we could avoid two EXEC calls:

EXEC (N'CREATE OR ALTER PROCEDURE #PrintMax(@iInput NVARCHAR(MAX))
AS
BEGIN
    IF @iInput IS NULL
    RETURN;

    DECLARE @ReversedData NVARCHAR(MAX)
          , @LineBreakIndex INT
          , @SearchLength INT;

    SET @SearchLength = 4000;

    WHILE LEN(@iInput) > @SearchLength
    BEGIN
    SET @ReversedData = LEFT(@iInput COLLATE DATABASE_DEFAULT, @SearchLength);
    SET @ReversedData = REVERSE(@ReversedData COLLATE DATABASE_DEFAULT);
    SET @LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13), @ReversedData COLLATE DATABASE_DEFAULT);
    PRINT LEFT(@iInput, @SearchLength - @LineBreakIndex + 1);
    SET @iInput = RIGHT(@iInput, LEN(@iInput) - @SearchLength + @LineBreakIndex - 1);
    END;

    IF LEN(@iInput) > 0
    PRINT @iInput;
END;');

db<>fiddle Demo

Lukasz Szozda
  • 162,964
  • 23
  • 234
  • 275
3
create procedure dbo.PrintMax @text nvarchar(max)
as
begin
    declare @i int, @newline nchar(2), @print varchar(max); 
    set @newline = nchar(13) + nchar(10);
    select @i = charindex(@newline, @text);
    while (@i > 0)
    begin
        select @print = substring(@text,0,@i);
        while (len(@print) > 8000)
        begin
            print substring(@print,0,8000);
            select @print = substring(@print,8000,len(@print));
        end
        print @print;
        select @text = substring(@text,@i+2,len(@text));
        select @i = charindex(@newline, @text);
    end
    print @text;
end
Adam Cypher
  • 387
  • 3
  • 6
2

Uses Line Feeds and spaces as a good break point:

declare @sqlAll as nvarchar(max)
set @sqlAll = '-- Insert all your sql here'

print '@sqlAll - truncated over 4000'
print @sqlAll
print '   '
print '   '
print '   '

print '@sqlAll - split into chunks'
declare @i int = 1, @nextspace int = 0, @newline nchar(2)
set @newline = nchar(13) + nchar(10)


while Exists(Select(Substring(@sqlAll,@i,3000))) and (@i < LEN(@sqlAll))
begin
    while Substring(@sqlAll,@i+3000+@nextspace,1) <> ' ' and Substring(@sqlAll,@i+3000+@nextspace,1) <> @newline
    BEGIN
        set @nextspace = @nextspace + 1
    end
    print Substring(@sqlAll,@i,3000+@nextspace)
    set @i = @i+3000+@nextspace
    set @nextspace = 0
end
print '   '
print '   '
print '   '
Saeed Zhiany
  • 2,051
  • 9
  • 30
  • 41
BickiBoy
  • 33
  • 5
1

My PrintMax version for prevent bad line breaks on output:


    CREATE PROCEDURE [dbo].[PrintMax](@iInput NVARCHAR(MAX))
    AS
    BEGIN
      Declare @i int;
      Declare @NEWLINE char(1) = CHAR(13) + CHAR(10);
      While LEN(@iInput)>0 BEGIN
        Set @i = CHARINDEX(@NEWLINE, @iInput)
        if @i>8000 OR @i=0 Set @i=8000
        Print SUBSTRING(@iInput, 0, @i)
        Set @iInput = SUBSTRING(@iInput, @i+1, LEN(@iInput))
      END
    END
Ercument Eskar
  • 132
  • 3
  • 15
0

Here's another version. This one extracts each substring to print from the main string instead of taking reducing the main string by 4000 on each loop (which might create a lot of very long strings under the hood - not sure).

CREATE PROCEDURE [Internal].[LongPrint]
    @msg nvarchar(max)
AS
BEGIN

    -- SET NOCOUNT ON reduces network overhead
    SET NOCOUNT ON;

    DECLARE @MsgLen int;
    DECLARE @CurrLineStartIdx int = 1;
    DECLARE @CurrLineEndIdx int;
    DECLARE @CurrLineLen int;   
    DECLARE @SkipCount int;

    -- Normalise line end characters.
    SET @msg = REPLACE(@msg, char(13) + char(10), char(10));
    SET @msg = REPLACE(@msg, char(13), char(10));

    -- Store length of the normalised string.
    SET @MsgLen = LEN(@msg);        

    -- Special case: Empty string.
    IF @MsgLen = 0
    BEGIN
        PRINT '';
        RETURN;
    END

    -- Find the end of next substring to print.
    SET @CurrLineEndIdx = CHARINDEX(CHAR(10), @msg);
    IF @CurrLineEndIdx BETWEEN 1 AND 4000
    BEGIN
        SET @CurrLineEndIdx = @CurrLineEndIdx - 1
        SET @SkipCount = 2;
    END
    ELSE
    BEGIN
        SET @CurrLineEndIdx = 4000;
        SET @SkipCount = 1;
    END     

    -- Loop: Print current substring, identify next substring (a do-while pattern is preferable but TSQL doesn't have one).
    WHILE @CurrLineStartIdx < @MsgLen
    BEGIN
        -- Print substring.
        PRINT SUBSTRING(@msg, @CurrLineStartIdx, (@CurrLineEndIdx - @CurrLineStartIdx)+1);

        -- Move to start of next substring.
        SET @CurrLineStartIdx = @CurrLineEndIdx + @SkipCount;

        -- Find the end of next substring to print.
        SET @CurrLineEndIdx = CHARINDEX(CHAR(10), @msg, @CurrLineStartIdx);
        SET @CurrLineLen = @CurrLineEndIdx - @CurrLineStartIdx;

        -- Find bounds of next substring to print.              
        IF @CurrLineLen BETWEEN 1 AND 4000
        BEGIN
            SET @CurrLineEndIdx = @CurrLineEndIdx - 1
            SET @SkipCount = 2;
        END
        ELSE
        BEGIN
            SET @CurrLineEndIdx = @CurrLineStartIdx + 4000;
            SET @SkipCount = 1;
        END
    END
END
redcalx
  • 8,177
  • 4
  • 56
  • 105
0

This should work properly this is just an improvement of previous answers.

DECLARE @Counter INT
DECLARE @Counter1 INT
SET @Counter = 0
SET @Counter1 = 0
DECLARE @TotalPrints INT
SET @TotalPrints = (LEN(@QUERY) / 4000) + 1
print @TotalPrints 
WHILE @Counter < @TotalPrints 
BEGIN
-- Do your printing...
print(substring(@query,@COUNTER1,@COUNTER1+4000))

set @COUNTER1 = @Counter1+4000
SET @Counter = @Counter + 1
END
vinbhai4u
  • 1,329
  • 3
  • 19
  • 36
0

If the source code will not have issues with LF to be replaced by CRLF, No debugging is required by following simple codes outputs.

--http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
--Bill Bai
SET @SQL=replace(@SQL,char(10),char(13)+char(10))
SET @SQL=replace(@SQL,char(13)+char(13)+char(10),char(13)+char(10) )
DECLARE @Position int 
WHILE Len(@SQL)>0 
BEGIN
SET @Position=charindex(char(10),@SQL)
PRINT left(@SQL,@Position-2)
SET @SQL=substring(@SQL,@Position+1,len(@SQL))
end; 
0

If someone interested I've ended up as generating a text file with powershell, executing scalar code:

$dbconn = "Data Source=sqlserver;" + "Initial Catalog=DatabaseName;" + "User Id=sa;Password=pass;"
$conn = New-Object System.Data.SqlClient.SqlConnection($dbconn)
$conn.Open()

$cmd = New-Object System.Data.SqlClient.SqlCommand("
set nocount on

DECLARE @sql nvarchar(max) = ''

SELECT 
    @sql += CHAR(13) + CHAR(10) + md.definition + CHAR(13) + CHAR(10) + 'GO'
FROM sys.objects AS obj
join sys.sql_modules AS md on md.object_id = obj.object_id
join sys.schemas AS sch on sch.schema_id = obj.schema_id
where obj.type = 'TR'


select @sql
", $conn)
$data = [string]$cmd.ExecuteScalar()


$conn.Close()

$data | Out-File -FilePath "C:\Users\Alexandru\Desktop\bigstring.txt"

This script it's for getting a big string with all the triggers from the DB.